MyBatis操作数据库(入门)

本节目标

  • 使用MyBatis完成简单的增删改查操作,参数传递
  • 掌握MyBatis的两种写法:注解和XML方式
  • 掌握MyBatis相关的日志配置

前言

        在应用分层学习中,我们了解web应用程序一般分为三层,即Controller、Service、Dao。在之前的案例中,请求流程如下:浏览器发起请求,先请求Controller,Controller接收到请求之后,调用Service进行业务逻辑处理,Service再调用Dao,但是Dao层的数据是Mock的,真实的数据应该从数据库中读取。

        我们学习MySQL数据库时,已经学习了JDBC来操作数据库,但是JDBC操作太复杂了。

JDBC操作示例回顾

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

从上述流程可以看出,对于JDBC来说,整个操作非常的繁琐,那么有没有一种方法,可以更简单、更方便的操作数据库呢?


目录

前言

一、什么是MyBatis?

二、MyBatis入门 

2.1 准备工作

2.1.1 创建工程

2.1.2 数据准备 

2.2 配置数据库连接字符串 

2.3 写持久层代码

三、MyBatis的基本操作 

3.1 打印日志 

3.2 参数传递 

3.3 增(Insert) 

3.4 删(Delete)

3.5 改(update) 

3.6 查(select)

3.6.1 起别名

3.6.2 结果映射 

3.6.3 开启驼峰命名(推荐) 

四、MyBatis XML配置文件 

4.1 配置连接字符串和MyBatis

4.2 写持久层代码 

4.2.1 添加mapper接口

4.2.2 添加UserinfoXmlMapper.xml 

4.3 增删改查操作 

4.3.1 增(Insert)

4.3.2 删(Delete)

4.3.3 改(Update) 

4.3.4 查(Select)

五、多表查询 

六、#{}和${}

6.1 #{}和${}使用

6.2 #{}和${}区别 

6.3 #{}的优势 

6.4 排序功能

6.5 like查询


一、什么是MyBatis?

  • MyBatis是一款优秀的持久层框架,用于简化JDBC的开发
  • MyBatis本是Apache的一个开源项目iBatis,2010年这个项目由apache迁移到google code,并改名为MyBatis。

持久层:指的就是持久化操作的层,通常指数据访问层(dao),用来操作数据库的。

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

二、MyBatis入门 

MyBatis操作数据库的步骤:

  • 准备工作(创建springboot工程、数据库表准备,实体类)
  • 引入MyBatis的相关依赖,配置MyBtis(数据库连接信息)
  • 编写SQL语句(注解/XML)
  • 测试

2.1 准备工作

2.1.1 创建工程

创建springbboot工程,并导入MyBatis的依赖,MySQL的驱动包

2.1.2 数据准备 

创建用户表,并创建对应的实体类UserInfo

-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;-- 使用数据数据
USE mybatis_test;-- 创建表[用户表]
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`username` VARCHAR ( 127 ) NOT NULL,`password` VARCHAR ( 127 ) NOT NULL,`age` TINYINT ( 4 ) NOT NULL,`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默认',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; -- 添加用户信息
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

创建对应的实体类UserInfo

import lombok.Data;import java.util.Date;@Data
public class Userinfo {private Integer id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;private Date createTime;private Date updateTime;}

2.2 配置数据库连接字符串 

MyBatis中要连接数据库,需要数据库相关参数配置

  • MySQL驱动类
  • 登录名
  • 密码
  • 数据库连接字符串 

 如果是application.yml文件,配置内容如下:

#数据库连接配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver

2.3 写持久层代码

在项目中,创建持久层接口UserInfoMapper 

import com.example.MyBatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface UserInfoMapper {@Select("select * from userinfo")List<UserInfo> getUserInfoAll();
}

MyBatis的持久层接口规范一般都叫xxxMapper

@Mapper注解:表示是MyBatis中的Mapper接口

  • 程序运行时,框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理
  • @Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容

使用IDEA自动生成测试类

1、在需要测试的Mapper接口中,右键->Generate->Test

2、书写测试代码

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid getUserInfoAll() {System.out.println(userInfoMapper.getUserInfoAll());}
}

测试类上添加了注解@SpringBootTest,在测试类在运行时,就会自动加载Spring的运行环境。我们通过@Autowired这个注解,注入我们要测试的类,就可以开始进行测试了。

运行结果如下:

三、MyBatis的基本操作 

        上面我们学习了MyBatis的查询操作,接下来我们学习MyBatis的增,删,改操作,在学习这些操作之前,我们先来学习MyBatis日志打印。

3.1 打印日志 

在MyBatis当中我们可以借助日志,查看sql语句的执行、执行传递的参数以及执行结果。

在配置文件中进行配置即可

mybatis:configuration: # 配置打印 MyBatis⽇志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

重新运行程序,可以看到SQL执行内容,以及传递参数和执行结果。

3.2 参数传递 

需求:查询id=2的用户,对应的SQL语句就是:select * from userinfo where id = 2

但是这样的话,只能查找id=2的数据,所以SQL语句中的id值不能写成固定的值,需要变为动态的数值。解决办法:在方法中添加一个参数,将方法中的参数,传给SQL语句,使用#{}的方式获取方法中的参数

@Select("select * from userinfo where id = #{id}")List<UserInfo> getUserInfoById(Integer id);

注意:如果mapper接口方法形参只有一个普通类型的参数,#{}里面的属性名可以随便写。建议还是和参数名保持一致。

添加测试用例

@Testvoid getUserInfoById() {System.out.println(userInfoMapper.getUserInfoById(2));}

运行结果:

也可以通过@Param,设置参数的别名,如果使用@Param设置别名,#{}里面的属性名必须和@Param设置一样

@Select("select * from userinfo where id = #{iid}")List<UserInfo> getUserInfoById2(@Param("iid") Integer id);

如果#{}里面的属性名必须和@Param设置不一样,我们运行代码。

运行结果:  

3.3 增(Insert) 

Mapper接口

@Options(useGeneratedKeys = true, keyProperty = "id")@Insert("insert into userinfo (username, password, age, gender) " +"values (#{username}, #{password}, #{age}, #{gender})")Integer insert(UserInfo userInfo);

直接使用UserInfo对象的属性名来获取参数

测试代码:

@Testvoid insert() {UserInfo userinfo = new UserInfo();userinfo.setUsername("zzz");userinfo.setPassword("123");userinfo.setAge(18);userinfo.setGender(1);Integer result = userInfoMapper.insert(userinfo);System.out.println("result: " + result + ", id" + userinfo.getId());}

运行结果:

返回主键

Insert语句默认返回的是受影响的行数,但是在有些情况下,数据插入之后,还需要有后续的关联操作,需要获取到新插入数据的id

比如订单系统

当我们下完订单之后,需要通知物流系统,这时就需要拿到订单id

如果想要拿到自增id,需要在Mapper接口上添加一个Options的注解

  • useGeneratedKeys:这会令MyBatis使用JDBC的getGeneratedKeys方法来取出数据库内部生成的主键
  • keyProperty:指定能够唯一识别对象的属性,MyBatis会使用getGeneratedKeys的返回值或insert语句的selectKey子元素设置它的值

3.4 删(Delete)

Mapper接口

@Delete("delete from userinfo where id = #{id}")Integer delete(Integer id);

测试用例

@Testvoid delete() {System.out.println(userInfoMapper.delete(10));}

运行截图 

3.5 改(update) 

Mapper接口

@Update("update userinfo set username = #{username} where id = #{id}")Integer update(UserInfo userInfo);

测试用例

@Testvoid update() {UserInfo userInfo = new UserInfo();userInfo.setUsername("李四");userInfo.setId(9);System.out.println(userInfoMapper.update(userInfo));}

 运行截图

3.6 查(select)

Mapper接口

@Select("select * from userinfo")List<UserInfo> getUserInfoAll();

测试用例

@Testvoid getUserInfoAll() {System.out.println(userInfoMapper.getUserInfoAll());}

运行截图

 观察数据库中的字段

从运行结果上可以看出,我们SQL语句中,查询了delete_flag,create_flag,update_time,这几个字字段在数据库中是有赋值的,但是在控制台中显示为空 ,这是为什么呢

原因分析: 

当自动映射查询结果时,MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写)。这意味着如果发现了ID列和id属性,MyBatis会将ID列的值赋给id属性。

注意: 

MyBatis会根据方法的返回结果进行赋值。

方法用对象Userinfo接收返回结果,MySQL查询出来数据为一条,就会自动赋值给对象。

方法用List<Userinfo>接收返回结果,MySQL查询出来数据为一条或多条时,也会自动赋值给List。

但如果MySQL查询结果返回多条,但是方法返回值使用Userinfo接收,MyBatis执行就会报错。

解决上述为null的办法:

  1. 起别名
  2. 结果映射
  3. 开启驼峰命名

3.6.1 起别名

Mapper接口

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

测试代码

@Testvoid queryAllUser() {System.out.println(userInfoMapper.queryAllUser());}

运行截图

3.6.2 结果映射 

Mapper接口

@Results(id = "resultMap", value = {@Result(column = "delete_flag", property = "deleteFlag"),@Result(column = "create_time", property = "createTime"),@Result(column = "update_time", property = "updateTime")})@Select("select * from userinfo")List<UserInfo> queryAllUser2();

测试代码

@Testvoid queryAllUser2() {System.out.println(userInfoMapper.queryAllUser2());}

运行截图

如果其它SQL,也希望可以复用这个映射关系,可以用上述代码定义的名称resultMap

@Select("select * from userinfo")
@ResultMap(value = "resultMap")
List<UserInfo> queryAllUser3();

3.6.3 开启驼峰命名(推荐) 

        通常数据库列使用蛇形命名法进行命名(下划线分割各个单词),而Java属性一般遵循驼峰命名法约定。为了在这两种方法之间启动自动映射,需要将mapUnderscoreToCamelCase设置为true

mybatis:configuration: map-underscore-to-camel-case: true

四、MyBatis XML配置文件 

MyBatis的开发有两种方式:

  1. 注解
  2. XML

上面学习了注解的方式,接下来我们学习XML的方式

使用MyBatis的注解方式,主要是来完成一些简单的增删查改功能,如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。

MyBatis XML的方式需要以下两步

  • 配置数据库连接字符串和MyBatis
  • 写持久层代码

4.1 配置连接字符串和MyBatis

此步骤需要进行两项设置,数据库连接字符串设置和MyBatis的XML文件配置。

application.yml文件,配置内容如下:

#数据库连接配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Drivermybatis:mapper-locations: classpath:mapper/**Mapper.xmlconfiguration: # 配置打印 MyBatis⽇志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true

配置mybatis xml文件路径,在resources/mapper下创建的所有XML文件 

4.2 写持久层代码 

持久层代码分两部分

  1. 方法定义interface
  2. 方法实现:XXX.xml

4.2.1 添加mapper接口

数据库持久层的接口定义:

import com.example.MyBatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface UserinfoXmlMapper {List<UserInfo> queryAllUser();
}

4.2.2 添加UserinfoXmlMapper.xml 

数据库持久层的实现,MyBatis的固定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="com.example.MyBatis.mapper.UserinfoXmlMapper"></mapper>

创建UserInfoXmlMapper.xml,路径参考yml中的配置

查询所有用户的具体实现:

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

以下是对以上标签的说明:

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

4.3 增删改查操作 

4.3.1 增(Insert)

UserInfoMapper接口:

Integer insertUser(Userinfo userinfo);

UserInfoMapper.xml实现:

<insert id="insertUser">insert into userinfo (username, password, age, gender, phone) values (#{username},#{password}, #{age}, #{gender}, #{phone})
</insert>

4.3.2 删(Delete)

UserInfoMapper接口:

Integer deleteUser(Integer id);

UserInfoMapper.xml实现:

<delete id="deleteUser">delete from userinfo where id = #{id}
</delete>

4.3.3 改(Update) 

UserInfoMapper接口:

Integer updateUser(Userinfo userinfo);

 UserInfoMapper.xml实现:

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

4.3.4 查(Select)

        同样的,使用XML的方式进行查询,也存在数据封装的问题,我们把SQL语句进行简单修改,查询更多的字段内容。

<select id="queryAllUser" resultType="com.example.mybatis.model.Userinfo">select id, username, password, age, gender, phone, delete_flag, create_time, update_timefrom userinfo
</select>

运行截图:

 结果显示:deleteFlag, createTime, updateTime也没有赋值

解决办法和注解类似:

  • 起别名
  • 结果映射
  • 开启驼峰命名

 重点讲下xml来写结果映射

Mapper.xml

<resultMap id="BaseMap" type="com.example.mybatis.model.Userinfo"><id column="id" property="id"></id><result column="delete_flag" property="deleteFlag"></result><result column="create_time" property="createTime"></result><result column="update_time" property="updateTime"></result></resultMap><select id="queryAllUser" resultMap="BaseMap" resultType="com.example.mybatis.model.Userinfo">select id, username, password, age, gender, phone, delete_flag, create_time, update_timefrom userinfo</select>

五、多表查询 

多表查询和单表查询类似,只是SQL不同而已

实体类

import lombok.Data;import java.util.Date;@Data
public class Userinfo {private Integer id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;private Date createTime;private Date updateTime;
}

Mapper接口

import com.example.mybatis.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface ArticleInfoXmlMapper {ArticleInfo selectArticleAndUserById(Integer id);
}

ArticleInfoXmlMapper.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="com.example.mybatis.mapper.ArticleInfoXmlMapper"><select id="selectArticleAndUserById" resultType="com.example.mybatis.model.ArticleInfo">select article.*, user.username, user.gender from articleinfo articleleft join userinfo useron article.uid = user.idwhere article.id = #{id}</select>
</mapper>

运行结果截图

 

六、#{}和${}

MyBatis参数赋值有两种方式,前面使用了#{}进行赋值,接下来我们看下两者的区别

6.1 #{}和${}使用

1、先看Interger类型的参数

@Select("select username, password, age, gender, phone from userinfo where id = #{id}")Userinfo queryById4(Integer id);

观察打印日志

 

        我们输出的参数并没有在后面拼接,id的值是使用?进行占位,这种SQL我们称之为“预编译SQL”。 

我们把#{}改成${}再观察打印的日志:

@Select("select username, password, age, gender, phone from userinfo where id = ${id}")Userinfo queryById5(Integer id);

观察打印日志

 

可以看出,这次参数是直接拼接在SQL语句中了。

2.接下来我们再看String类型的参数

@Select("select username, password, age, gender, phone from userinfo where username = #{name}")Userinfo queryByName(String name);

观察打印日志:

 

我们把#{}改成${}再观察打印的日志:

@Select("select username, password, age, gender, phone from userinfo where username = ${name}")Userinfo queryByName2(String name);

观察打印日志:

 

        可以看出,这次的参数依然是直接拼接在SQL语句中了,但是字符串作为参数时,需要添加引号'',使用${}不会拼接引号'',导致程序报错。

修改代码如下:

@Select("select username, password, age, gender, phone from userinfo where username = '${name}'")Userinfo queryByName2(String name);

再运行观察打印日志:

 

从上面两个例子可以看出:

  • #{}使用的是预编译SQL,通过?占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中,#{}会根据参数类型,自动拼接引号''
  • ${}会直接进行字符替换,一起对SQL进行编译,如果参数为字符串,需要加上引号''

注意:参数为数字类型时,也可以加上,查询结果不变,但是可能会导致索引失效,性能下降。

6.2 #{}和${}区别 

#{}和${}的区别就是预编译SQL即时SQL的区别

简单回顾:

当客户发送一条SQL语句给服务器后,大致流程如下:

  1. 解析语法和语义,校验SQL语句是否正确
  2. 优化SQL语句,制定执行计划
  3. 执行并返回结果

一条SQL如果走上述流程处理,我们称之为Immediate Statements(即时SQL)

6.3 #{}的优势 

1、性能更高

        绝大多数情况下,某一条SQL语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如select的where子句值不同,update的set子句值不同,insert的values值不同),如果每次都需要经过上面的语法解析,SQL优化、SQL编译等,则效率就明显不行了。

 

        预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同),省去了解析优化等过程,以此来提高效率。 

2、更安全(防止SQL注入) 

        SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。

        由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。

SQL注入代码:' or 1 = '1'

先来看看SQL注入的例子

@Select("select username, password, age, gender, phone from userinfo where username = '${name}'")List<Userinfo> queryByName3(String name);

测试代码:

正常访问情况:

@Testvoid queryByName3() {System.out.println(userInfoMapper.queryByName3("admin"));}

结果运行截图

SQL注入场景

​​@Testvoid queryByName3() {System.out.println(userInfoMapper.queryByName3("' or 1 = '1"));}

结果依然被正确查询出来了,其中参数or被当成了SQL语句的一部分

可以看出,查询的数据并不是自己想要的数据,所以用于查询的字段,尽量使用#{}预查询的方式 

        SQL注入是一种非常常见的数据库攻击手段,SQL注入漏洞也是网络世界中最普遍的漏洞之一。

        如果发生在用户登陆的场景中,密码输入为' or 1 = ' 1,就可能完成登陆(不是一定会发生的场景,需要看登陆代码如何写)

6.4 排序功能

        从上面的例子中,可以得出结论:${}会有SQL注入的风险,所以我们尽量使用#{}完成查询,既然如此,是不是${}就没有存在的必要性呢?

当然不是

接下来我们看下${}的使用场景

Mapper实现

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +"from userinfo order by id ${sort}")List<Userinfo> queryAllUserBySort(String sort);

使用${sort}可以实现排序查询,而使用#{sort}就不能实现排序查询了。 

        注意:此处sort参数为String类型,但是SQL语句中,排序规则是不需要加引号''的,所以此时的${sort}也不加引号。

我们把${}改成#{}

运行结果:

可以发现,使用#{sort}查询时,desc前后自动给加了引号,导致sql错误

#{}会根据参数类型判断是否拼接引号,如果参数类型为String,就会加上引号。

除此之外,还有表明作为参数时,也只能使用${} 

6.5 like查询

like使用#{}报错

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +"from userinfo where username like '%#{key}%'")List<Userinfo> queryAllUserByLike(String key);

把#{}改成${}可以正确查出来,但是${}存在SQL注入的问题,所以不能直接使用${}。

解决办法:使用MySQL的内置函数concat()来处理,实现代码如下:

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +"from userinfo where username like concat('%', #{key}, '%'})")List<Userinfo> queryAllUserByLike(String key);

 

 

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

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

相关文章

化学SCI期刊,中科院4区,易录用,几乎不退稿

一、期刊名称 Chemical Papers 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;化学 影响因子&#xff1a;2.1 中科院分区&#xff1a;4区 三、期刊征稿范围 该杂志致力于基础和应用化学和化学工程研究。它的范围很广&#xff0c;涵盖了所有化学科学&…

2024年江苏智能制造工厂名单:我看出了未来择业和跳槽方向

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 在当今这个飞速发展的时代&#xff0c;智能制造已成为推动工业进步的强大引擎。随着技术革新的浪潮一波接一波地涌来&#xff0c;我们不禁要问&a…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-49风格迁移

49风格迁移 读入内容图像&#xff1a; import torch import torchvision from torch import nn import matplotlib.pylab as plt import liliPytorch as lp from d2l import torch as d2l# 读取内容图像 content_img d2l.Image.open(../limuPytorch/images/rainier.jpg) plt.…

使用 Swift 递归搜索目录中文件的内容,同时支持 Glob 模式和正则表达式

文章目录 前言项目设置查找文件读取CODEOWNERS文件解析规则搜索匹配的文件确定文件所有者输出结果总结前言 如果你新加入一个团队,想要快速的了解团队的领域和团队中拥有的代码库的详细信息。 如果新团队中的代码库在 GitHub / GitLab 中并且你不熟悉代码所有权模型的概念或…

Unity开箱即用的UGUI面板的拖拽移动功能

文章目录 &#x1f449;一、背景&#x1f449;二、效果图&#x1f449;三、原理&#x1f449;四、核心代码&#x1f449;五&#xff0c;总结 &#x1f449;一、背景 之前做PC项目时常常有面板拖拽移动的需求&#xff0c;今天总结封装一下&#xff0c;做成一个随时随地可复用的…

Linux 安装 Redis 教程

优质博文&#xff1a;IT-BLOG-CN 一、准备工作 配置gcc&#xff1a;安装Redis前需要配置gcc&#xff1a; yum install gcc如果配置gcc出现依赖包问题&#xff0c;在安装时提示需要的依赖包版本和本地版本不一致&#xff0c;本地版本过高&#xff0c;出现如下问题&#xff1a…

Windows 11 安装 安卓子系统 (WSA)

How to Install Windows Subsystem for Android (WSA) on Windows 11 新手教程&#xff1a;如何安装Windows 11 安卓子系统 说明 Windows Subsystem for Android 或 WSA 是由 Hyper-V 提供支持的虚拟机&#xff0c;可在 Windows 11 操作系统上运行 Android 应用程序。虽然它需…

python基础_类

在Python中&#xff0c;类&#xff08;Class&#xff09;是面向对象编程&#xff08;OOP&#xff09;的核心概念之一。类提供了一种创建新对象的模板&#xff0c;这些对象通常被称为类的实例或对象。以下是关于Python类的一些关键点和特性&#xff1a; 定义类 类通过class关键…

ctfshow-web入门-命令执行(web71-web74)

目录 1、web71 2、web72 3、web73 4、web74 1、web71 像上一题那样扫描但是输出全是问号 查看提示&#xff1a;我们可以结合 exit() 函数执行php代码让后面的匹配缓冲区不执行直接退出。 payload&#xff1a; cvar_export(scandir(/));exit(); 同理读取 flag.txt cinclud…

文华财经博易大师盘立方多空波段止损画线指标公式

TT:PERIOD7; EMA120:EMA(C,120); RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100; K:SMA(RSV,3,1); D:SMA(K,3,1); J:3*K-2*D; DRAWTEXT(TT&&J<0,L,多),VALIGN0; DRAWTEXT(TT&&J>100,H,空),VALIGN2; IF(TT,EMA(C,60),NULL),RGB(255,255,2…

JavaScript数组对象 , 正则对象 , String对象以及自定义对象介绍

1. Array数组对象 数组对象是使用单独的变量名来存储一系列的值。 1.1创建一个数组 创建一个数组&#xff0c;有三种方法。 【1】常规方式: let 数组名 new Array();【2】简洁方式: 推荐使用 let 数组名 new Array(数值1,数值2,...);【3】字面:在js中创建数组使用中括号…

试用笔记之-收钱吧安卓版演示源代码,收钱吧手机版感受

首先下载&#xff1a; https://download.csdn.net/download/tjsoft/89499105 安卓手机安装 如果有收钱吧帐号输入收钱吧帐号和密码。 如果没有收钱吧帐号点我的注册 登录收钱吧帐号后就可以把手机当成收钱吧POS机用了&#xff0c;还可以扫客服的付款码哦 源代码技术交流QQ:42…

U盘数据恢复实战指南:原因、方案与预防措施

一、引言&#xff1a;U盘数据恢复概述 在数字化时代&#xff0c;U盘作为一种便携式存储设备&#xff0c;广泛应用于个人和企业中。然而&#xff0c;由于各种原因&#xff0c;U盘数据丢失的问题时有发生。U盘数据恢复技术便是在这种情况下应运而生&#xff0c;它帮助用户在数据…

TPS61085非同步650kHz,1.2MHz, 18.5V升压DCDC芯片

1 特点 TPS61085外观和丝印PMKI 2.3 V 至 6 V 输入电压范围 具有 2.0A 开关电流的 18.5V 升压转换器 650kHz/1.2MHz 可选开关频率 可调软启动 热关断 欠压闭锁 8引脚VSSOP封装 8引脚TSSOP封装 2 应用 手持设备 GPS接收器 数码相机 便携式应用 DSL调制解调器 PCMCIA卡 TFT LCD…

Java并发编程基础知识点

目录 Java并发编程基础知识点1、线程&#xff0c;进程概念及二者的关系进程相关概念线程相关概念进程与线程的关系补充小知识点&#xff1a; 2、线程的状态Java线程的状态&#xff1a;Java线程不同状态之间的切换图示 3、Java程序中如何创建线程&#xff1f;①、继承Thread类②…

【漏洞复现】用友NC——文件上传漏洞

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 用友NC是由用友公司开发的一套面向大型企业和集团型企业的管理软…

C语言 | Leetcode C语言题解之第207题课程表

题目&#xff1a; 题解&#xff1a; bool canFinish(int numCourses, int** prerequisites, int prerequisitesSize, int* prerequisitesColSize) {int** edges (int**)malloc(sizeof(int*) * numCourses);for (int i 0; i < numCourses; i) {edges[i] (int*)malloc(0);…

【Linux】部署NFS服务实现数据共享

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

4-数据提取方法2(xpath和lxml)(6节课学会爬虫)

4-数据提取方法2&#xff08;xpath和lxml&#xff09;&#xff08;6节课学会爬虫&#xff09; 1&#xff0c;Xpath语法&#xff1a;&#xff08;1&#xff09;选择节点&#xff08;标签&#xff09;&#xff08;2&#xff09;“//”:能从任意节点开始选择&#xff08;3&#xf…

计算机网络面试TCP篇之TCP三次握手与四次挥手

TCP 三次握手与四次挥手面试题 任 TCP 虐我千百遍&#xff0c;我仍待 TCP 如初恋。 巨巨巨巨长的提纲&#xff0c;发车&#xff01;发车&#xff01; PS&#xff1a;本次文章不涉及 TCP 流量控制、拥塞控制、可靠性传输等方面知识&#xff0c;这些知识在这篇&#xff1a; TCP …