当前位置: 首页 > news >正文

Mybatis-基础操作

                Mybatis的基础操作就是通过Mybatis完成对数据的增删改查。我们通过例子来引入这些操作,之前的项目较久远,因此我们从零开始进行准备工作:

搭建项目

一、创建数据库user_list并插入数据:

-- 创建数据库
create table user_list
(id          int unsigned primary key auto_increment comment '主键ID',username    varchar(100) comment '用户名',password    varchar(32) default '123456' comment '密码',gender      tinyint unsigned comment '性别,1男2女',job         tinyint unsigned comment '部门ID',creat_time  datetime not null comment '创建时间',update_time datetime not null comment '修改时间'
) comment '基础信息表';
INSERT INTO user_list (username, password, gender, job, creat_time, update_time)
VALUES ('张三', 'zhangsan_pwd123', 1, 1, NOW() - INTERVAL 14 DAY, NOW()),('李四', 'lisi_secure_pwd', 2, 2, NOW() - INTERVAL 13 DAY, NOW()),('王五', 'wangwu_123456', 1, 3, NOW() - INTERVAL 12 DAY, NOW()),('赵六', 'zhaoliu_password', 2, 4, NOW() - INTERVAL 11 DAY, NOW()),('孙七', 'sunqi_pwd_2023', 1, 5, NOW() - INTERVAL 10 DAY, NOW()),('周八', 'zhouba_secure', 2, 1, NOW() - INTERVAL 9 DAY, NOW()),('吴九', 'wujiu_123', 1, 2, NOW() - INTERVAL 8 DAY, NOW()),('郑十', 'zhengshi_pwd', 2, 3, NOW() - INTERVAL 7 DAY, NOW()),('王十一', 'wangshiyi_securepwd', 1, 4, NOW() - INTERVAL 6 DAY, NOW()),('李十二', 'lishier_password1', 2, 5, NOW() - INTERVAL 5 DAY, NOW()),('张十三', 'zhangshisan_pwd2', 1, 1, NOW() - INTERVAL 4 DAY, NOW()),('刘十四', 'liushisi_secure3', 2, 2, NOW() - INTERVAL 3 DAY, NOW()),('陈十五', 'chenshiwu_1234', 1, 3, NOW() - INTERVAL 2 DAY, NOW()),('高十六', 'gaoshiliu_pwd5', 2, 4, NOW() - INTERVAL 1 DAY, NOW()),('陆十七', 'lushiqi_securepwd6', 1, null, NOW(), NOW());

二、创建一个新的Springboot项目,并引入对应的起步依赖(Mybatis、mysql驱动、lombok):

三、在默认配置文件application.properties中引入数据库连接信息:

#application.properties文件
spring.application.name=MybatisTest
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_database
#连接数据库的用户名
spring.datasource.username=root
#连接数据库密码
spring.datasource.password=1234

四、创建对应的实体类UserList(定义java类采用驼峰命名法):

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;/*author CHN*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserList {private int id;    // 主键IDprivate String username;    // 用户名private String password;    // 密码private int gender;    // 性别,1表示男,2表示女private int job;    // 部门IDprivate LocalDateTime creatTime;    // 创建时间private LocalDateTime updateTime;    // 修改时间
}

五、创建对应的Mapper接口UserMapper:

import org.apache.ibatis.annotations.Mapper;/*author CHN*/
@Mapper
public interface UserMapper {
}

最终效果: 

删除

        前端发送删除请求以删除数据,为了明确要删除哪一条基础,我们通常选择的唯一的主键来查询并删除。
        明确目标后在UserMapper接口中定义方法:先添加注解删除所需注解@Delete(),括号内补充对应的sql语句并以""包裹,并补充方法(方法体暂时略,下文介绍):

@Mapper
public interface UserMapper {@Delete("delete from user_list where id=17")public void deleteUserById();
}

        但此时我们只能删除id为17的元素,不能满足需求,我们需要使该参数能动态的变化,即根据方法所接收的参数而变化,这里就要用到Mybatis提供的可变参数形式#{},{}内填入对应的参数。为方便测试,我们仍在测试文件中编写代码:

//UserMapper接口————————————————————————————
@Mapper//将其作为bean注入ioc
public interface UserMapper {
//    根据ID删除数据
@Delete("delete from user_list where id=#{targetId}")//可变参数
public void deleteUserById(Integer targetId);
}
//测试类————————————————————————————————————
@SpringBootTest
class MybatisTestApplicationTests {@Autowired//使用ioc中的bean创建实例private UserMapper userMapper;@Testvoid test() {userMapper.deleteUserById(15);}
}

        执行test方法,返回数据库并刷新即可看到id为15的那条数据被删除

        注意deleteUserById方法因为我们无需返回值所以设置为void方法,但实际上它是有返回值的,其返回值代表此次操作操作影响了几条数据。

        如果该接口方法只传入一个普通参数,那#{}中的属性名可以随便写,但不建议知道就好

        此时数据已被成功删除,但Mybatis底层到底执行了什么sql语句,执行结果是什么我们并不知道。在Mybatis框架中,我们可以借助日志来查看这一信息,日志默认是关闭的,想要开启则需在配置文件application.properties添加相关语句:

#配置Mybatis的日志,并将其输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

        之后再执行,可以看到控制台明显多了些语句,其中最重要的语句为:

==>  Preparing: delete from user_list where id=?
==> Parameters: 16(Integer)
<==    Updates: 1 
  • 第一行中的"?"为参数占位符,代表可变参数
  • 第二行代表将Inreger类的参数16传入
  • 第三行代表影响了一条数据

预编译SQL

        这种使用了可变参数的方式为预编译SQL,即在数据库服务器上预编译一条SQL语句,并在之后的操作中多次绑定不同的参数来执行这条语句。这种方法不仅可以提高SQL执行的效率,还能增强安全性,特别是防止SQL注入攻击。

        我们先来介绍如何提高执行效率:
提高效率:
        我们java项目中编写sql语句后,需先连接上数据库并发送语句,数据库接收语句后还要进行语法解析、优化SQL、编译SQL才能执行。为了提高效率,其会将前三步结束后的数据缓存下来,如果下次执行相同语句则可跳过前三步,直接执行。
        之前写的SQL语句preparing部分(Parameters部分为空):

delete from user_list where id=16;
delete from user_list where id=17;
delete from user_list where id=18;

        每次执行语句都需执行一遍前三步,效率较低,使用可变参数后SQL语句preparing部分(Parameters部分依次为16(Integer)、17(Integer)、18(Integer)):

delete from user_list where id=?;-- 16、17、18依次传入替代?

        因为语句相同,所以前三步只需执行一次即可,16、17、18依次传入并执行,与之前的执行结果相同。再来看防止SQL注入攻击:

SQL注入攻击

        SQL注入攻击的原理是利用输入字段或URL参数等,将恶意SQL语句插入到应用程序的查询中。由于应用程序在执行SQL查询时,直接将用户输入的数据拼接到查询字符串中,未经充分验证或转义,攻击者可以通过构造特定的输入,使数据库执行意外的SQL语句。

        我们来看一个例子:登录页面输入账号和密码,正确通过,错误则不通过,对应sql语句preparing部分为(Parameters部分为空):

select count(*) from user_list where username='用户名'and password='密码';

        如果该数据正确则返回1,不正确则返回0,但如果在输入密码时为'or '1'='1呢,此时日志中SQL语句就会变为:

==>  Preparing: select count(*) from user_list where username='用户名'and password=''or '1'='1';
==> Parameters: 
<==    Columns: count(*)
<==        Row: 20
<==      Total: 1

        密码的判断部分永远为真,程序判断失效,这就是一个简单的SQL注入。

        再来看预编译SQL(即使用了可变参数),日志中其sql语句就会变为:

==>  Preparing: select count(*) from user_list where username=? and password=? ;
==> Parameters: 用户名(String),'or '1'='1(String)
<==    Columns: count(*)
<==        Row: 0
<==      Total: 1

        可以看到有两个'?',代表两个可变参数,Parameters部分的参数作为String传入,此时传入的参数就不会影响到程序执行,这就是防止SQL注入攻击。

参数占位符不止#{...}还有${...}

#{...}执行时,会将其替换成?并生成预编译SQL语句,后续参数自动替换。

${...}将该参数直接拼接在SQL语句中,存在一定的SQL注入风险,一般对表名或字段名进行设置时使用。

增加

        先根据表结构编写执行插入操作的sql语句(因为id为自增,所以无需添加):

INSERT INTO user_list (username, password, gender, job, creat_time, update_time)
VALUES ('刘涛', 'liutao_password1', 1, 1, NOW() - INTERVAL 12 DAY, NOW() - INTERVAL 12 DAY),('王芳', 'wangfang_securepwd2', 2, 2, NOW() - INTERVAL 11 DAY, NOW() - INTERVAL 11 DAY);

        再在UserMapper接口中添加对应方法和注解@Insert():

@Insert("INSERT INTO user_list (username, password, gender, job, creat_time, update_time)" +"VALUES ('刘涛', 'liutao_password1', 1, 1, NOW() - INTERVAL 12 DAY, NOW() - INTERVAL 12 DAY)," +"       ('王芳', 'wangfang_securepwd2', 2, 2, NOW() - INTERVAL 11 DAY, NOW() - INTERVAL 11 DAY);")
public void insertUsers();

         与上文相同,这样的参数不可变,需变为可变参数,也可以和上文一样声明参数并传递,但因为参数较多,会导致代码可读性较弱难以维护,因此我们选择将这些参数封装到一个对象中,即封装到之前创建的UserList类对象中:

@Insert("INSERT INTO user_list (username, password, gender, job, creat_time, update_time)" +"VALUES (#{username},#{password},#{gender},#{job},#{creatTime},#{updateTime});" )
public void insertUsers(UserList userList);

        这样方法就已编写好了,我们继续在测试类中模拟前端发起请求:

    @Testvoid test() {//自行添加无id参数的构造函数UserList target = new UserList("王芳", "wangfang_securepwd2", 2, 2, LocalDateTime.now(), LocalDateTime.now());userMapper.insertUsers(target);}
//控制台输出
==>  Preparing: INSERT INTO user_list (username, password, gender, job, creat_time, update_time)VALUES (?,?,?,?,?,?);
==> Parameters: 王芳(String), wangfang_securepwd2(String), 2(Integer), 2(Integer), 2024-11-27T22:08:06.845000400(LocalDateTime), 2024-11-27T22:08:06.845000400(LocalDateTime)
<==    Updates: 1

        可以看到可变参数和参数一一对应并传入,实现了增加数据。

主键返回

        主键返回指的是在数据库表中插入一条新记录后,将该记录的主键值返回。
        我们以表多对多的关系为例,中间表关联着A和B表的id,当我们在A表新建一条数据时,需要得到该数据id的值并返回给中间表,以便建立多对多的关系。

        实现方法:在添加数据的接口方法上添加注解@Options:

//UserMapper
@Mapper
public interface UserMapper {@Options(useGeneratedKeys = true,keyProperty = "id")@Insert("INSERT INTO user_list (username, password, gender, job, creat_time, update_time)" +"VALUES (#{username},#{password},#{gender},#{job},#{creatTime},#{updateTime});" )public void insertUsers(UserList userList);
}
//test类中添加getid()
@Test
void test() {UserList target = new UserList("测试用例", "wangfang_securepwd2", 2, 2, LocalDateTime.now(), LocalDateTime.now());userMapper.insertUsers(target);System.out.println(target.getId());
}
//控制台输出
<==    Updates: 1
Closing non transactional SqlSession 
50

        执行结果显示控制台输出了新插入的数据的id为50。 

修改

        与删除类似:前端发送修改请求以修改数据,为了明确要修改哪一条基础,我们通常选择的唯一的主键来查询并修改。
        先根据表结构编写执行修改操作的sql语句:

update user_list
set username='',password='',gender='',job='',update_time='',username=''
where id = 33;

        再在UserMapper接口中添加对应方法和注解@Update(),与添加字段相同,我们将需要的参数封装到userList对象中:

//UserMapper接口————————————————————————————
@Update("update user_list set username=#{username},password=#{password},gender=#{gender},job=#{job},update_time=#{updateTime} where id = #{id};")
public void updateUsers(UserList userList);
//测试类————————————————————————————————————@Testvoid testUpdate() {UserList target = new UserList(33,"测试用例", "test2", 2, 2, LocalDate.now(), LocalDateTime.now());userMapper.updateUsers(target);}
//控制台输出
==>  Preparing: update user_list set username=?,password=?,gender=?,job=?,update_time=? where id = ?;
==> Parameters: 测试用例(String), test2(String), 2(Integer), 2(Integer), 2024-11-28T01:39:21.615929600(LocalDateTime), 33(Integer)
<==    Updates: 1

        执行testUpdate方法,返回数据库并刷新即可看到id为33的那条数据被修改。

查询      

        同样根据唯一的主键来查询,执行查询操作的sql语句:

select *
from user_list where id=38;

        再在UserMapper接口中添加对应方法和注解@Select(),注意之前的方法返回值都是void,查询方法则应返回对应的类:

//UserMapper接口————————————————————————————@Select("select * from user_list where id=#{id};")public UserList getUserById(Integer id);
//测试类————————————————————————————————————@Testvoid testSelect() {UserList target = userMapper.getUserById(38);System.out.println(target);}
//控制台输出
==>  Preparing: select * from user_list where id=?;
==> Parameters: 38(Integer)
<==    Columns: id, username, password, gender, job, creat_time, update_time
<==        Row: 38, 郑十, zhengshi_pwd, 2, 3, 2024-11-19 22:53:11, 2024-11-26 22:53:11
<==      Total: 1
UserList(id=38, username=郑十, password=zhengshi_pwd, gender=2, job=3, creatTime=null, updateTime=null)

        此时数据虽已被查询出来,但最后两个字段却为空,这是因为有关该字段java类中和数据库中的名字不一样,一个为creatTime,一个为creat_time。
        因为如果实体类属性名和数据库查询返回的字段名一样,Mybayis会自动封装,不一样则不会封装。

        有两种较笨重的解决方法:

  1. 在sql语句中给名称不一样的字段起别名。
  2. 添加注解@Results和@Result。
//方法一:起别名使字段名相同
@Select("select id, username, password, gender, job," +"creat_time creatTime, update_time updateTime from user_list where id=#{id};")
public UserList getUserById(Integer id);
//方法二:添加注解Results
@Results({@Result(column="creat_time",property="creatTime"),@Result(column="update_time",property="updateTime")
})
@Select("select * from user_list where id=#{id};")
public UserList getUserById(Integer id);
//控制台输出
UserList(id=38, username=郑十, password=zhengshi_pwd, gender=2, job=3, creatTime=2024-11-19, updateTime=2024-11-26T22:53:11)

        @Results()注解内部需要一个数组,数组内部则为@Result注解,@Result注解中column为原先的字段名,property为java类中的属性名。

        这两种方法都比较臃肿,我们知道就好。
        接下来介绍第三种方法:开启Mybatis的驼峰命名自动映射开关。

        想要开启也需在配置文件application.properties添加相关语句:

#开启Mybatis的驼峰命名自动映射开关
mybatis.configuration.map-underscore-to-camel-case=true

        这样类似于creat_time的字段就会自动被修改为creatTime。

条件查询

        先来看sql语句:查询userList中名称中包含”十“,gender为1,job为3或5的人,查询结果根据创建时间倒序排序:

select *
from user_list
where username like '%十%'and gender = 1and job in (3, 5)
order by creat_time desc;

        再在UserMapper接口中添加对应方法和注解@Select(),因为可能返回多条数据,所以应定义一集合接收返回数据,但要注意username为%十%,按照之前的方法应为username like '%#{name}%',但#{}不允许出现在''之中1,所以我们应使用${}替换:

//UserMapper接口————————————————————————————@Select("select * from user_list where username like '%${name}%' and gender = #{gender} and job in (#{job1}, #{job2}) order by creat_time desc;")public List<UserList> targetList(String name,int gender,int job1,int job2);
//测试类————————————————————————————————————@Testvoid testSelect() {List<UserList> targetList = userMapper.targetList("十", 1, 3, 5);System.out.println(targetList);}
//控制台输出
==>  Preparing: select * from user_list where username like '%十%' and gender = ? and job in (?, ?) order by creat_time desc;
==> Parameters: 1(Integer), 3(Integer), 5(Integer)
<==    Columns: id, username, password, gender, job, creat_time, update_time
<==        Row: 45, 陆十七, lushiqi_securepwd6, 1, 5, 2024-11-26 22:53:11, 2024-11-26 22:53:11
<==        Row: 43, 陈十五, chenshiwu_1234, 1, 3, 2024-11-24 22:53:11, 2024-11-26 22:53:11
<==      Total: 2
[UserList(id=45, username=陆十七, password=lushiqi_securepwd6, gender=1, job=5, creatTime=2024-11-26, updateTime=2024-11-26T22:53:11), 
UserList(id=43, username=陈十五, password=chenshiwu_1234, gender=1, job=3, creatTime=2024-11-24, updateTime=2024-11-26T22:53:11)]

        但使用了${}又会导致性能低、sql注入等问题, 我们可以通过concat字符拼接函数来解决。

CONCAT

        CONCAT是一个字符串连接函数,它主要用于将两个或多个字符串连接成一个字符串,语法为:CONCAT(string1, string2, ..., stringN)。
        使用该函数就可以将%十%拆分为三部分,这样就可以使用#{}了:

//修改前:@Select("select * from user_list where username like '%${name}%' and gender = #{gender} and job in (#{job1}, #{job2}) order by creat_time desc;")public List<UserList> targetList(String name,int gender,int job1,int job2);
//修改后:@Select("select * from user_list where username like concat('%',#{name},'%') and gender = #{gender} and job in (#{job1}, #{job2}) order by creat_time desc;")public List<UserList> targetList(String name,int gender,int job1,int job2);

参数名说明

        在SpringBoot的2.X版本,只需注意参数名和#{}中的参数名相同即可,但在早期版本,系统会将参数名编译为var1、var2,这时就需要使用注解@Param来标明该字段对应的#{}中的字段:

//老版本:@Delete("delete from user_list where id=#{targetId}")public void deleteUserById(@Param("targetId") Integer Id);
//现版本:@Delete("delete from user_list where id=#{targetId}")public void deleteUserById(Integer Id);

        在上篇文章的引入的第三部分编写sql语句我们提到过:编写语句实现Mybatis有两种方式1、通过注解实现Mybatis,2、通过xml实现Mybatis,第一种已经在上文中讲过了,接下来看第二种方法。

XML映射文件

        xml文件放在对应的resourse文件下,在Mybatis中定义xml映射文件需要遵守规范:

一、XML映射文件的名称与Mapper接口名称一致,且XML映射文件和Mapper接口应放在相同的对应包下(同包同名)。

        右键Resources-New-Directory-输入org/example/mybatistest/Mapper-右键新建文件-New-FIle-UserMapperxml:

        在定义java类时我们通常采用"."来标示不同级的目录,例如package1.package2.package3,但在resources中我们需要以"/"分隔,例如package1/package2/package3,其次就是需要保证两文件同包同名:

二、XML文件的namespace属性需与Mapper接口的全类名保持一致。

        从Mybatis官网复制xml文件,并修改namesapce属性为自己的全类名(在UserMapper接口中右键接口名-Copy Reference即可获取全类名):复制"探究已映射的 SQL 语句"部分代码icon-default.png?t=O83Ahttps://mybatis.net.cn/getting-started.html

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mybatistest.Mapper.UserMapper"><select id="targetList"></select>
</mapper>

三、XML文件中的sql语句中的id需与Mapper接口中的方法名保持一致,并保持返回类型一致。

<!--UserMapper.xml文件:-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mybatistest.Mapper.UserMapper"><select id="targetList" resultType="org.example.mybatistest.pojo.UserList">select *from user_listwhere username like concat('%', #{name}, '%')and gender = #{gender}and job in (#{job1}, #{job2})order by creat_time desc;</select>
</mapper>
//UserMapper.java文件
@Mapper
public interface UserMapper {public List<UserList> targetList(String name,int gender,int job1,int job2);
}
//测试文件:
@SpringBootTest
class MybatisTestApplicationTests {@Autowiredprivate UserMapper userMapper;@Testvoid testSelect() {List<UserList> targetList = userMapper.targetList("十", 1, 3, 5);System.out.println(targetList);}
}

        因为在UserMapper.java文件下方法名为targetList,因此select id="targetList" ,又因为返回类型需要保持一致,resultType即结果类型,应写全类名,所以resultType="org.example.mybatistest.pojo.UserList">,此时再执行testSelect即可看到返回值相同。

        为什么要遵守这三点规范:我们通过Mybatis操作数据库是通过调用接口中的方法,再执行与该方法对应的sql语句即可完成操作,难点在于怎么找到与方法对应的sql语句。
        之前通过注解操作时执行方法就是执行在方法上方注解中的sql语句,但使用xml文件时方法和sql语句是分开的,因此我们需要根据接口中的方法名找到对应的sql语句。
        执行了以上三点规范执行方法后,Mybatis会自动查找“namespsce”与“该方法所在接口文件的全类名”相同的xml映射文件,并查找id与方法名相同的sql语句并执行,返回值为“resultType”与“该类所在文件的全类名”相同的类。

        但因为方法和sql语句不在同一个地方,一旦有多个方法对应的语句会使我们难以判断相应的方法和sql语句在哪,MyBatisX插件可以帮助我们解决这一问题:

MyBatisX插件

        MyBatisX为IntelliJ IDEA中的一款插件,它旨在简化MyBatis框架的使用,提升开发效率。MyBatisX能够根据数据库表结构自动生成Mapper接口、Mapper XML文件和Java实体类。这减少了手动编写重复代码的工作量,提高了开发效率。

        并且该插件还会在接口类和xml文件左侧加上一一对应的快捷跳转按钮,以便我们查找对应语句在哪:

        对于是使用注解还是xml映射,官方已对其做出说明:

        使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

        选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。

有关namespace

        在之前版本的 MyBatis 中,命名空间(Namespaces)的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。

        命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定。就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。

        命名解析:为了减少输入量,MyBatis 对所有具有名称的配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。

  • 全限定名(比如 “com.mypackage.MyMapper.selectAllThings)将被直接用于查找及使用。
  • 短名称(比如 “selectAllThings”)如果全局唯一也可以作为一个单独的引用。 如果不唯一,有两个或两个以上的相同名称(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”),那么使用时就会产生“短名称不唯一”的错误,这种情况下就必须使用全限定名。

相关文章:

Mybatis-基础操作

Mybatis的基础操作就是通过Mybatis完成对数据的增删改查。我们通过例子来引入这些操作&#xff0c;之前的项目较久远&#xff0c;因此我们从零开始进行准备工作&#xff1a; 搭建项目 一、创建数据库user_list并插入数据&#xff1a; -- 创建数据库 create table user_list …...

【工具】JS解析XML并且转为json对象

【工具】JS解析XML并且转为json对象 <?xml version1.0 encodingGB2312?> <root><head><transcode>hhhhhhh</transcode></head><body><param>ccccccc</param><param>aaaaaaa</param><param>qqqq<…...

软件测试技术面试题及参考答案整理

一、什么是兼容性测试?兼容性测试侧重哪些方面? 参考答案&#xff1a; 兼容测试主要是检查软件在不同的硬件平台、软件平台上是否可以正常的运行&#xff0c;即是通常说的软件的可移植性。 兼容的类型&#xff0c;如果细分的话&#xff0c;有平台的兼容&#xff0c;网络兼…...

Python学习36天

面向对象编程综合 # 创建父类 class Employee:# 创建私有属性__name None__salary None# 创建构造器初始化属性def __init__(self, __name, __salary):self.__name __nameself.__salary __salarydef get_annual(self):# 返回员工年薪return self.__salary * 12# 创建公共方…...

C语言——海龟作图(对之前所有内容复习)

一.问题描述 海龟作图 设想有一只机械海龟&#xff0c;他在C程序控制下在屋里四处爬行。海龟拿了一只笔&#xff0c;这支笔或者朝上&#xff0c;或者朝下。当笔朝下时&#xff0c;海龟用笔画下自己的移动轨迹&#xff1b;当笔朝上时&#xff0c;海龟在移动过程中什么也不画。 …...

关于如何在k8s中搭建一个nsfw黄图鉴定模型

随着现在应用内图片越来越多&#xff0c;安全审查也是必不可少的一个操作了 下面手把手教你如何将huggingface中的黄图检测模型部署到自己的服务器上去 1.找到对应的模型 nsfw_image_detection 2.在本地先验证如何使用 首先安装transformers python库 pip install transform…...

istio结合wasm插件的实际应用

在 Istio 中&#xff0c;WASM 插件的常见使用场景和功能包括以下几个方面&#xff1a; 1. 流量管理与请求修改 请求与响应头处理&#xff1a;动态添加、删除或修改 HTTP 请求或响应头。URL 重写&#xff1a;根据特定规则调整请求的路径或参数。请求路由增强&#xff1a;实现复…...

日志logrus

https://blog.csdn.net/m0_70982551/article/details/143095729 https://blog.csdn.net/wslyk606/article/details/81670713 https://www.bilibili.com/opus/1002468521099132928 地鼠文档&#xff1a;https://www.topgoer.cn/docs/goday/goday-1crg2adjknouc 极客文档&#xf…...

11.29 代码随想录Day45打卡(动态规划)

115.不同的子序列 题目&#xff1a;给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数。 题解&#xff1a; class Solution:def numDistinct(self, s: str, t: str) -> int:dp [[0] * (len(t) 1) for _ in range(len(s) 1)]for i in range…...

springboot336社区物资交易互助平台pf(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 社区物资交易互助平台设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff…...

【Maven】Nexus私服

6. Maven的私服 6.1 什么是私服 Maven 私服是一种特殊的远程仓库&#xff0c;它是架设在局域网内的仓库服务&#xff0c;用来代理位于外部的远程仓库&#xff08;中央仓库、其他远程公共仓库&#xff09;。一些无法从外部仓库下载到的构件&#xff0c;如项目组其他人员开发的…...

【python量化教程】如何使用必盈API的股票接口,获取最新分时KDJ数据

分时KDJ数据简介 股票分时 KDJ 数据是用于分析股票盘中短期走势的指标。它由未成熟随机指标 RSV 计算出 K 值、D 值、J 值。取值范围上&#xff0c;K 和 D 是 0 - 100&#xff0c;J 值可超出此范围。20 以下为超卖区、80 以上是超买区。关键信号有金叉&#xff08;预示上涨&am…...

DI依赖注入详解

DI依赖注入 声明了一个成员变量&#xff08;对象&#xff09;之后&#xff0c;在该对象上面加上注解AutoWired注解&#xff0c;那么在程序运行时&#xff0c;该对象自动在IOC容器中寻找对应的bean对象&#xff0c;并且将其赋值给成员变量&#xff0c;完成依赖注入。 AutoWire…...

mysql sql语句 between and 是否边界值

在 MySQL 中&#xff0c;使用 BETWEEN 运算符时&#xff0c;边界值是包括在内的。这意味着 BETWEEN A AND B 查询会返回 A 和 B 之间的所有值&#xff0c;包括 A 和 B 自身。 示例 假设有一个表 employees&#xff0c;其中有一个 salary 列&#xff0c;您可以使用以下查询&am…...

飞塔防火墙只允许国内IP访问

飞塔防火墙只允许国内IP访问 方法1 新增地址对象&#xff0c;注意里面已经细分为中国内地、中国香港、中国澳门和中国台湾 方法2 手动新增国内IP的对象组&#xff0c;目前好像一共有8632个&#xff0c;每个对象最多支持600个IP段...

宠物之家:基于SpringBoot的领养平台

第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。时代进步的标志&#xff0c;就是让人们过上更好的生活。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不…...

golang 实现比特币内核:如何接入 RPC 后端获得特定交易的二进制数据

我们非常关注解析比特币的二进制数据,这使得我们的工作看起来是可行的。比特币是一个分布式网络系统,这意味着它需要全球各地的节点协同工作,甚至比特币核心库也需要连接其他节点来帮助它,就像查询交易费一样。 世界上没有免费的午餐。当你使用比特币系统进行交易时,你需…...

QML学习 —— 34、视频媒体播放器(附源码)

效果 说明 您可以单独使用MediaPlayer播放音频内容(如音频),也可以将其与VideoOutput结合使用以渲染视频。VideoOutput项支持未转换、拉伸和均匀缩放的视频演示。有关拉伸均匀缩放演示文稿的描述,请参见fillMode属性描述。 播放可能出错问题 出现的问题:      DirectS…...

宝塔Linux面板上传PHP文件或者修改PHP文件,总是转圈圈,其他文件正常,解决办法

目录 问题描述 寻找解决方案 1.重启宝塔面板 2.清理宝塔缓存 3.升级面板 4.ssh远程 5.清空回收站 6.换网络 7. IDE远程编辑 总结&#xff1a; 问题描述 一直用宝塔linux面板&#xff0c;感觉非常好用&#xff0c;点点就能搞定&#xff0c;环境也很好配置。 公司搬家&…...

Flink——进行数据转换时,报:Recovery is suppressed by NoRestartBackoffTimeStrategy

热词统计案例&#xff1a; 用flink中的窗口函数&#xff08;apply&#xff09;读取kafka中数据&#xff0c;并对热词进行统计。 apply:全量聚合函数&#xff0c;指在窗口触发的时候才会对窗口内的所有数据进行一次计算&#xff08;等窗口的数据到齐&#xff0c;才开始进行聚合…...

贪心算法题目合集

贪心算法题目合集 1319&#xff1a;【例6.1】排队接水 贪心策略思想 1319&#xff1a;【例6.1】排队接水 贪心策略思想 1319&#xff1a;【例6.1】排队接水 贪心算法与其说是算法&#xff0c;不如说是一种风格&#xff1a;每次做事情都选择自己认为的最优解。 贪心算法的题很…...

NSSCTF-做题笔记

[羊城杯 2020]easyre 查壳&#xff0c;无壳&#xff0c;64位&#xff0c;ida打开 encode_one encode_tow encode_three 那么我们开始一步一步解密&#xff0c;从最外层开始 def decode_three(encrypted_str):decrypted_str ""for char in encrypted_str:char_code …...

SpringBoot源码-spring boot启动入口ruan方法主线分析(一)

一、SpringBoot启动的入口 1.当我们启动一个SpringBoot项目的时候&#xff0c;入口程序就是main方法&#xff0c;而在main方法中就执行了一个run方法。 SpringBootApplication public class StartApp {public static void main(String[] args) {// testSpringApplication.ru…...

python json.dump()和json.dumps()的区别

用人话总结一下 json.dump()是针对文件的json和python的转换 json.dumps()主要是针对内容数据 json.dumps(obj, skipkeysFalse, ensure_asciiTrue, check_circularTrue, allow_nanTrue, clsNone, indentNone, separatorsNone, encoding“utf-8”, defaultNone, sort_keysFalse…...

快速排序hoare版本和挖坑法(代码注释版)

hoare版本 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>// 交换函数 void Swap(int* p1, int* p2) {int tmp *p1;*p1 *p2;*p2 tmp; }// 打印数组 void _printf(int* a, int n) {for (int i 0; i < n; i) {printf("%d ", a[i]);}printf("…...

ELK(Elasticsearch + logstash + kibana + Filebeat + Kafka + Zookeeper)日志分析系统

文章目录 前言架构软件包下载 一、准备工作1. Linux 网络设置2. 配置hosts文件3. 配置免密登录4. 设置 NTP 时钟同步5. 关闭防火墙6. 关闭交换分区7. 调整内存映射区域数限制8. 调整文件、进程、内存资源限制 二、JDK 安装1. 解压软件2. 配置环境变量3. 验证软件 三、安装 Elas…...

SpringBoot中忽略实体类中的某个属性不返回给前端的方法

使用Jackson的方式&#xff1a; //第一种方式&#xff0c;使用JsonIgnore注解标注在属性上&#xff0c;忽略指定属性 public class PropertyDTO {JsonProperty("disable")private Integer disable;JsonProperty("placeholder")private String placeholde…...

Flink中普通API的使用

本篇文章从Source、Transformation&#xff08;转换因子&#xff09;、sink这三个地方进行讲解 Source&#xff1a; 创建DataStream本地文件SocketKafka Transformation&#xff08;转换因子&#xff09;&#xff1a; mapFlatMapFilterKeyByReduceUnion和connectSide Outpu…...

【人工智能】从零构建一个文本分类器:用Python和TF-IDF实现

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 文本分类是自然语言处理(NLP)领域的基础任务之一,广泛应用于垃圾邮件检测、情感分析和新闻分类等场景。本篇文章从零开始,通过详细讲解 TF-IDF 特征提取方法,以及如何将其与机器学习算法结合,实现一…...

原型模式

功能&#xff1a;复制一个运行时的对象&#xff0c;包括对象各个成员当前的值。并且能够通过父类的指针来克隆出子类的对象 主要解决&#xff1a;在运行期建立原型 优点&#xff1a;性能提高、避免了构造函数的约束 步骤&#xff1a; 1、定义抽象原型&#xff0c;声明纯虚接…...

基于FPGA的FM调制(载波频率、频偏、峰值、DAC输出)-带仿真文件-上板验证正确

基于FPGA的FM调制-带仿真文件-上板验证正确 前言一、FM调制储备知识载波频率频偏峰值个人理解 二、代码分析1.模块分析2.波形分析 总结 前言 FM、AM等调制是学习FPGA信号处理一个比较好的小项目&#xff0c;通过学习FM调制过程熟悉信号处理的一个简单流程&#xff0c;进而熟悉…...

open-instruct - 训练开放式指令跟随语言模型

文章目录 关于 open-instruct设置训练微调偏好调整RLVR 污染检查开发中仓库结构 致谢 关于 open-instruct github : https://github.com/allenai/open-instruct 这个仓库是我们对在公共数据集上对流行的预训练语言模型进行指令微调的开放努力。我们发布这个仓库&#xff0c;并…...

Java爬虫:获取1688商品详情接口的技术实现与代码示例

引言 1688作为中国领先的B2B电子商务平台&#xff0c;拥有海量的商品信息。对于商家和市场研究人员来说&#xff0c;能够从1688获取商品详情信息&#xff0c;对于市场分析、竞品研究等具有重要价值。本文将介绍如何使用Java编写爬虫&#xff0c;以合法、高效的方式获取1688商品…...

详解Rust泛型用法

文章目录 基础语法泛型与结构体泛型约束泛型与生命周期泛型与枚举泛型和Vec静态泛型(const 泛型)类型别名默认类型参数Sized Trait与泛型常量函数与泛型泛型的性能 Rust是一种系统编程语言&#xff0c;它拥有强大的泛型支持&#xff0c;泛型是Rust中用于实现代码复用和类型安全…...

Spring Boot拦截器(Interceptor)详解

拦截器Interceptor 拦截器我们主要分为三个方面进行讲解&#xff1a; 介绍下什么是拦截器&#xff0c;并通过快速入门程序上手拦截器拦截器的使用细节通过拦截器Interceptor完成登录校验功能 1. 快速入门 什么是拦截器&#xff1f; 是一种动态拦截方法调用的机制&#xff…...

STM32-- 看门狗--介绍、使用场景、失效场景

STM32 中的看门狗&#xff08;Watchdog Timer&#xff0c;简称 WDG&#xff09;有两种主要类型&#xff1a;独立看门狗&#xff08;IWDG&#xff09; 和 窗口看门狗&#xff08;WWDG&#xff09;。它们的喂狗机制各有特点&#xff0c;主要区别如下&#xff1a; 1. 独立看门狗&a…...

Perplexica - AI 驱动的搜索引擎

更多AI开源软件&#xff1a; AI开源 - 小众AIhttps://www.aiinn.cn/sources Perplexica 是一个开源的 AI 驱动的搜索工具或 AI 驱动的搜索引擎&#xff0c;可以深入互联网寻找答案。受 Perplexity AI 的启发&#xff0c;它是一个开源选项&#xff0c;不仅可以搜索网络&#xf…...

Linux笔记--基于OCRmyPDF将扫描件PDF转换为可搜索的PDF

1--官方仓库 https://github.com/ocrmypdf/OCRmyPDF 2--基本步骤 # 安装ocrmypdf库 sudo apt install ocrmypdf# 安装简体中文库 sudo apt-get install tesseract-ocr-chi-sim# 转换 # -l 表示使用的语言 # --force-ocr 防止出现以下错误&#xff1a;ERROR - PriorOcrFoundE…...

MySQL聚合查询分组查询联合查询

#对应代码练习 -- 创建考试成绩表 DROP TABLE IF EXISTS exam; CREATE TABLE exam ( id bigint, name VARCHAR(20), chinese DECIMAL(3,1), math DECIMAL(3,1), english DECIMAL(3,1) ); -- 插入测试数据 INSERT INTO exam (id,name, chinese, math, engli…...

ffmpeg 预设的值 加速

centos 安装ffmpeg 编译安装 官网获取最新的linux ffmpeg 代码 https://ffmpeg.org//releases/ mkdir -p /data/app/ffmpeg cd /data/app/ffmpeg wget http://www.ffmpeg.org/releases/ffmpeg-7.1.tar.gz tar -zxvf ffmpeg-7.1.tar.gz#安装所需的编译环境 yum install -y \…...

Spring Boot 与 Spring Cloud Alibaba 版本兼容对照

版本选择要点 Spring Boot 3.x 与 Spring Cloud Alibaba 2022.0.x Spring Boot 3.x 基于 Jakarta EE&#xff0c;javax.* 更换为 jakarta.*。 需要使用 Spring Cloud 2022.0.x 和 Spring Cloud Alibaba 2022.0.x。 Alibaba 2022.0.x 对 Spring Boot 3.x 的支持在其发行说明中…...

解决爬虫ConnectionResetError出现的问题

提问 使用python进行网络爬虫出现ConnectionResetError如何解决&#xff1f; 解答 遇到ConnectionResetError错误时&#xff0c;通常是因为远程服务器端主动重置了连接。常见原因包括请求频率过高、网络问题或触发了防爬虫机制。为解决该问题&#xff0c;可以采取以下方法&a…...

Rust学习笔记_03——元组

Rust学习笔记_01——基础 Rust学习笔记_02——数组 Rust学习笔记_03——元组 文章目录 Rust学习笔记_03——元组元组1. 定义元祖2. 访问元组中的元素3. 元组的解构4. 元组不可遍历和切片5. 元组作为函数返回值6. 单元元组7. 代码演示 元组 在Rust编程语言中&#xff0c;元组&a…...

win10安装MySQL8.0.40,含踩坑记录

这里写自定义目录标题 win10安装MySQL8下载安装包配置环境变量初始化MySQL创建data文件夹初始化配置文件安装MySQL服务初始化创建root用户启动服务设置root用户密码登录验证 踩坑&#xff1a;MySQL 服务正在启动 ...MySQL 服务无法启动。服务没有报告任何错误。请键入 NET HELP…...

python+django自动化平台(一键执行sql) 前端vue-element展示

一、开发环境搭建和配置 pip install mysql-connector-pythonpip install PyMySQL二、django模块目录 dbOperations ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-313.pyc │ ├── admin.cpython-313.pyc │ ├── apps.cpython-313.pyc │ …...

【计算机网络】核心部分复习

目录 交换机 v.s. 路由器OSI七层更实用的TCP/IP四层TCPUDP 交换机 v.s. 路由器 交换机-MAC地址 链接设备和设备 路由器- IP地址 链接局域网和局域网 OSI七层 物理层&#xff1a;传输设备。原始电信号比特流。数据链路层&#xff1a;代表是交换机。物理地址寻址&#xff0c;交…...

urllib3只支持OpenSSL1.1.1

1 现象 urllib3 v2.0 only supports OpenSSL 1.1.1, currently the ssl module is compiled with OpenSSL 1.1.0j 20 Nov 2018.2 解决方法 降低urllib3的版本。 从pycharm中&#xff0c;先卸载原有的urllib3版本。 菜单“File|Settings|Project:python|Project Interprete…...

简单web项目自定义部署Dockerfile

本意就是弄清楚如何做web自定义项目的镜像。 基础镜像是java:8u261-jdk&#xff0c;其中java路径为/opt/java webdemo1.0.0.1-SNAPSHOT.jar文件里面已经包含了lib文件。 可以设置PATH也可以不设置&#xff0c;但是建议设置JAVA_HOME FROM swr.cn-north-4.myhuaweicloud.com…...

apache实现绑定多个虚拟主机访问服务

1个网卡绑定多个ip的命令 ip address add 192.168.45.140/24 dev ens33 ip address add 192.168.45.141/24 dev ens33 在linux服务器上&#xff0c;添加多个站点资料&#xff0c;递归创建三个文件目录 分别在三个文件夹下&#xff0c;建立测试页面 修改apache的配置文件http.…...

svn 崩溃、 cleanup失败 怎么办

在使用svn的过程中&#xff0c;可能出现整个svn崩溃&#xff0c; 例如cleanup 失败的情况&#xff0c;类似于 这时可以下载本贴资源文件并解压。 或者直接访问网站 SQLite Download Page 进行下载 解压后得到 sqlite3.exe 放到发生问题的svn根目录的.svn路径下 右键呼出pow…...