Spring boot 知识整理

news2025/4/19 7:29:20

一、SpringBoot 背景内容梳理

  • SpringBoot是一个基于Spring框架的开源框架,用于简化Spring应用程序的初始搭建和开发过程。它通过提供约定优于配置的方式,尽可能减少开发者的工作量,使得开发Spring应用变得更加快速、便捷和高效。

  • SpringBoot的主要特点包括:

  1. 简化配置:SpringBoot遵循约定优于配置的原则,减少了传统Spring应用中的大量配置。它通过自动配置(auto-configuration)和起步依赖(starterdependencies)来简化项目的配置过程,让开发者可以快速搭建起一个可运行的Spring应用。
  2. 集成性强:SpringBoot提供了大量的开箱即用的特性和功能,如内嵌的Servlet容器(如Tomcat、Jetty或Undertow)、健康检查、指标监控等。它还整合了诸多常用的库和框架,如SpringData、SpringSecurity等,使得开发者可以快速构建出功能完善的应用。
  3. 微服务支持:SpringBoot非常适合用于构建微服务架构。它提供了丰富的支持,如通过SpringCloud进行微服务架构的开发,集成了服务发现、配置中心、负载均衡等功能,帮助开发者构建可伸缩、高可用的微服务系统。
  4. 内嵌服务器:SpringBoot可以将应用程序打包成一个可执行的JAR文件,并内置了常用的Servlet容器,如Tomcat、Jetty或Undertow。这样一来,开发者可以通过简单的java-jar命令来运行应用程序,而无需部署到外部应用服务器。
  5. 生态丰富:由于SpringBoot的广泛应用和强大生态系统,开发者可以轻松地使用各种扩展和插件,如Actuator、SpringBootDevTools等,提高开发效率和应用质量。
  • 从前端的api到我后端的接受再到数据库:
    • 发请求——>控制层接受——> 调用服务层的函数——>调用Repository——>找到对应的实体然后进行数据库的交互

其中额外设计的有 Response 的设计 ,方便前端对于后端返回数据的统一规范处理;
DTO类(用于封装数据,通常在层与层之间传递,避免直接暴露实体);
转换器的设计,方便后端设置哪些数据可以返回给对应的前端

二、Rest api 规范

  • 路径

    • 路径又称"终点”(endpoint),表示API的具体网址。
    • 在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。
  • Http 动词

    • GET(SELECT):从服务器取出资源(一项或多项)。
    • POST(CREATE):在服务器新建一个资源。
    • PUT(UPDATE):在服务器更新资源(将户端提供改变后的完整资源)。
    • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
    • DELETE(DELETE):从服务器删除资源。

三、JPA

JPA 提供了一种标准化的方式来进行对象关系映射,是 Java 应用程序中进行数据持久化的关键技术。通过使用 JPA,开发者可以更高效地管理数据库操作,专注于业务逻辑开发

JPA 是一个规范,多个框架实现了该规范,常见的实现包括:

  • Hibernate:最流行的 JPA 实现,提供丰富的功能和强大的社区支持。
  • EclipseLink:Oracle 官方的 JPA 实现,支持多种数据库。
  • OpenJPA:Apache 提供的 JPA 实现。

3.1. JPA内部有关的注解

1. @Entity

  • 定义:标识一个类为 JPA 实体,表示该类对应于数据库中的一张表。

2. @Table

  • 定义:指定实体类对应的数据库表的名称。
  • 用法@Table(name = "table_name")

3. @Id`

  • 定义:标识实体类的主键字段。

4. @GeneratedValue`

  • 定义:指定主键的生成策略。
  • 常用策略
    • GenerationType.IDENTITY:主键由数据库自动生成。
    • GenerationType.SEQUENCE:使用数据库序列生成主键。
    • GenerationType.AUTO:根据数据库自动选择生成策略。

5. @Column`

  • 定义:映射实体类的字段到数据库表的列。
  • 常用属性
    • name:指定数据库列名。
    • nullable:指定该列是否可以为 null
    • length:指定字符串列的最大长度。

6.@Builder

  • 用于简化对象的构建过程。它通过生成一个构建器模式的实现,减少频繁的属性赋值
  • 用法:
Student student = Student.builder() .name("Alice") .age(20).email("alice@example.com") .build();

3.2. JPA 的使用(JpaRepository<T, ID>)

  • JpaRepository<T, ID>是一个 Spring Data JPA 提供的接口,允许你执行 CRUD 操作。

    • 第一个泛型参数 T 表示实体类的类型,即与数据库相对应的Java类
    • 第二个泛型参数 ID 表示主键的类型,即用于唯一标识实体的字段,确保记录的唯一性(通常是 Long, Integer, String 等)
    • 必须遵循 JpaRepository<实体类, 主键类型> 的格式。交换位置会导致错误。
  • JPA的方法用法:

    • 基本 CRUD 操作
      • 通过继承 JpaRepository,可以获得基本的 CRUD 操作,如 save(), findById(), delete(), findAll() 等。
    • 查询方法命名约定
      • JPA 提供了一种约定优于配置的方法命名规则。你可以通过特定的命名规则自动生成查询。
      • 示例:
        • findByEmail(String email):根据 email 查找学生。
        • findByAgeBetween(int min, int max):查找年龄在 minmax 之间的学生。
        • findByNameStartingWith(String namePrefix):查找姓名以 namePrefix 开头的学生。
    • 使用 @Query 注解
      • 可以使用 @Query 注解来定义自定义查询。
      • 原始 SQL 查询
        • 使用 nativeQuery = true 指定这是一个原始 SQL 查询。
        • 示例:
@Query(value = "select * from Student   where  email = :email", nativeQuery = true)  
List<Student> findByEmail2(@Param("email") String email);

query里面的用 :email 做占位符,用来读取@Parm 中的形参

.
- JPQL 查询
- JPQL 用于面向对象的查询,允许你直接操作实体。
- 使用 SELECT NEW 创建新对象。
- 示例:`findByEmail3(@Param(“email”) String email)

@Query(value = "SELECT NEW com.demo.springbootstudy.dao.Student(s.name, s.email) FROM Student s WHERE s.email = :email")  
List<Student> findByEmail3(@Param("email") String email);

query里面的用 :email 做占位符,用来读取@Parm 中的形参

3.3 JpaSpecificationExecutor< T > 的使用

JpaSpecificationExecutor 是 Spring Data JPA 提供的接口,用于支持动态查询。通过该接口,可以使用 JPA Criteria API 构建复杂的查询条件.
.
在面对一个大项目,里面复杂的查询情况,可以单独创建一个文件夹存放不同实体的 Specification的静态定义,后续在服务层根据对应的情况,设置不同属性进行传参调用静态函数

使用:

  • 创建Repority接口去继承 JpaSpecificationExecutor< T >(其中T指的是数据库表实体)
  • 定义一个类存放Specification的静态方法,模板如下:
public static Specification<Student> filterByCriteria(StudentDTO studentDTO) {  
    return (root, query, cb) -> {  
        List<Predicate> predicates = new ArrayList<>();  
  
        if (studentDTO.getName() != null) {  
            predicates.add(cb.equal(root.get("name"), studentDTO.getName()));  
        }  
        if (studentDTO.getMinAge() != null) {  
            predicates.add(cb.greaterThanOrEqualTo(root.get("age"), studentDTO.getMinAge()));  
        }  
        if (studentDTO.getMaxAge() != null) {  
            predicates.add(cb.lessThanOrEqualTo(root.get("age"), studentDTO.getMaxAge()));  
        }  

		//两个 return 实现的效果一样
		return query.where(predicates.toArray(new Predicate[0])).getRestriction();
        return cb.and(predicates.toArray(new Predicate[0]));  
    };  
}

- `root`:表示查询的根实体,即在这里是 `Student`。
- `query`:表示查询的上下文。
- `cb`:表示构建条件的 `CriteriaBuilder`对象。
	- 其中这个cb里面集成多种方法用于构建查询条件
	- cb.and(predicates.toArray(new Predicate[0])); 
		- 动态地将所有的查询条件结合起来,形成一个整体的查询条件
- `Predicate`:表示查询条件,通过add函数来添加不同的查询条件
	- .getRestriction() 用于获取它所表示的条件
  • 后续调用先构建好对应的形参,然后调用上面的静态方法获取Specification;
  • 最后调用Repository接口中的函数findAll,形参传入的是上面的Specification对象;

3.4 分页查找的用法

四、Mybatis 的用法

MyBatis 是一个 持久层框架,用于简化数据库操作。它提供了一种映射关系,将数据库中的记录映射到 Java 对象,并支持通过 XML 或注解的方式配置 SQL 语句。
.
MyBatis Plus 是在 MyBatis 的基础上开发的增强工具包,旨在简化开发,提高效率。它提供了 CRUD 操作、条件构造器、分页插件、代码生成器等多种功能。

4.1 配置

Pom.xml 配置

<dependencies>
    <!-- MyBatis Plus 依赖 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.23</version>
    </dependency>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

  	<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>
    <!-- 其他依赖 -->
    <!-- ... -->
</dependencies>

Spring Boot 配置(以 .yml 文件为主)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: your_username
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl //sql打印到控制台

编写实体类

@Data
@TableName(value = "user")
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;

    private String name;

    private Integer age;

    private String email;

    // @TableField(fill = FieldFill.INSERT, value = "create_time")
    // private Long createTime;

    // @TableField(fill = FieldFill.INSERT_UPDATE, value = "update_time")
    // private Long updateTime;

    // @Version
    // private Long version;
}

编写Mapper接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

添加注解MapperScan

在Application类上添加@MapperScan注解并指定mapper的包路径

Mybatis-plus 拦截器配置

配置类用于添加拦截器,如乐观锁、分页等插件

@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { 
	MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 
	// 添加乐观锁拦截器 
	interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
	// 添加分页拦截器
	interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 
	return interceptor; 
	}
}

4.2 常用功能介绍

1. CRUD 操作

MyBatis Plus 提供了基础的 CRUD 方法,包括 insertdeleteupdateselect 等。

    @Test
    void testInsert() {
        User user = User.builder()
                .name("fengzhu")
                .age(18)
                .email("abc@a.com")
                .build();
        userMapper.insert(user);
    }

    @Test
    void testSelect() {
        User user = userMapper.selectById(24L);
        System.out.println(user.getName());
    }

    @Test
    void testUpdate() {
        User user = User.builder()
                .id(27L)
                .email("fasfas@a.com").build();

        int count = userMapper.updateById(user);
        System.out.println("update success:" + count);
    }

    @Test
    void testDelete() {
        userMapper.deleteById(24L);
    }

2. 条件构造器

条件构造器主要涉及到3个类,AbstractWrapperQueryWrapperUpdateWrapper

在AbstractWrapper中提供了非常多的方法用于构建WHERE条件,而QueryWrapper针对SELECT语句,提供了select()方法,可自定义需要查询的列,而UpdateWrapper针对UPDATE语句,提供了set()方法,用于构造set语句,条件构造器也支持lambda表达式。

AbstractWrapper中用于构建SQL语句中的WHERE条件的方法进行部分列举

  • eq:equals,等于
  • allEq:all equals,全等于
  • ne:not equals,不等于
  • gt:greater than ,大于 >
  • ge:greater than or equals,大于等于≥
  • lt:less than,小于<
  • le:less than or equals,小于等于≤
  • between:相当于SQL中的BETWEEN
  • notBetween
  • like:模糊匹配。like(“name”,“黄”),相当于SQL的name like ‘%黄%’
  • likeRight:模糊匹配右半边。likeRight(“name”,“黄”),相当于SQL的name like ‘黄%’
  • likeLeft:模糊匹配左半边。likeLeft(“name”,“黄”),相当于SQL的name like ‘%黄’
  • notLike:notLike(“name”,“黄”),相当于SQL的name not like ‘%黄%’
  • isNull
  • isNotNull
  • in
  • and:SQL连接符AND
  • or:SQL连接符OR
  • apply:用于拼接SQL,该方法可用于数据库函数,并可以动态传参
// 条件查询
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
List<User> users = userMapper.selectList(queryWrapper);

// 条件更新
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "Tom").set("age", 27);
userMapper.update(updateWrapper);

3. Condition

条件构造器的诸多方法中,均可以指定一个boolean类型的参数condition,用来决定该条件是否加入最后生成的WHERE语句中,比如

String name = "黄"; // 假设name变量是一个外部传入的参数
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like(StringUtils.hasText(name), "name", name);
// 仅当 StringUtils.hasText(name) 为 true 时, 会拼接这个like语句到WHERE中
// 其实就是对下面代码的简化
if (StringUtils.hasText(name)) {
	wrapper.like("name", name);
}

4. 实体对象作为条件

调用构造函数创建一个Wrapper对象时,可以传入一个实体对象。后续使用这个Wrapper时,会以实体对象中的非空属性,构建WHERE条件(默认构建等值匹配的WHERE条件,这个行为可以通过实体类里各个字段上的@TableField注解中的condition属性进行改变)

@Test
public void test3() {
    User user = new User();
    user.setName("abc");
    user.setAge(28);
    QueryWrapper<User> wrapper = new QueryWrapper<>(user);
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

5. allEq方法

  • allEq方法传入一个map,用来做等值匹配
  • 当allEq方法传入的Map中有value为null的元素时,默认会设置为is null
  • 若想忽略map中value为null的元素,可以在调用allEq时,设置参数boolean null2IsNull为false
@Test
public void test3() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    Map<String, Object> param = new HashMap<>();
    param.put("age", 40);
    param.put("name", null);
    wrapper.allEq(param);
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

6. lambda条件构造器

  • lambda条件构造器,支持lambda表达式,可以不必像普通条件构造器一样,以字符串形式指定列名,它可以直接以实体类的方法引用来指定列。
  • 像普通的条件构造器,列名是用字符串的形式指定,无法在编译期进行列名合法性的检查,这就不如lambda条件构造器来的优雅。
@Test
public void testLambda() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.like(User::getName, "黄").lt(User::getAge, 30);
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}


- `like(User::getName, "黄")`:表示查询 `User` 表中 `name` 字段包含"黄"的记录。
- `lt(User::getAge, 30)`:表示查询 `User` 表中 `age` 字段小于 30 的记录
更新操作
  • updateById(T entity)
    根据入参entity的id(主键)进行更新,对于entity中非空的属性,会出现在UPDATE语句的SET后面,即entity中非空的属性,会被更新到数据库,示例如下
	@Test
	public void testUpdate2() {
		User user = new User();
		user.setId(1L); // 假设要更新的用户 ID
		user.setName("新的名字"); // 只更新名字,其他属性保持不变
		userMapper.updateById(user);
	}

  • update(T entity, Wrapper<T> wrapper)
    根据实体entity和条件构造器wrapper进行更新,示例如下
	@Test
	public void testUpdate2() {
		User user = new User();
		user.setName("abc");
		LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
		wrapper.between(User::getAge, 26,31).likeRight(User::getName,"吴");
		userMapper.update(user, wrapper);
	}
删除操作

BaseMapper一共提供了如下几个用于删除的方法

  • deleteById 根据主键id进行删除
  • deleteBatchIds 根据主键id进行批量删除
  • deleteByMap 根据Map进行删除(Map中的key为列名,value为值,根据列和值进行等值匹配)
  • delete(Wrapper<T> wrapper) 根据条件构造器Wrapper进行删除

7. 自定义Sql

  • 注解
	@Select("select * from user")
	List<User> selectRaw();
  • 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.mp.mappers.UserMapper">
	<select id="selectRaw" resultType="com.example.mp.po.User">
        SELECT * FROM user
    </select>
</mapper>


public interface UserMapper extends BaseMapper<User> {
	List<User> selectRaw();
}

8. 分页插件

  • 配置分页插件,在 MybatisPlusConfig 类中添加分页插件配置。
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
        // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
        return interceptor;
    }
  • 在查询时使用 Page 对象进行分页。
    @Test
    public void testPage() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(User::getAge, 28);
        // 设置分页信息, 查第3页, 每页2条数据
        Page<User> page = new Page<>(3, 2);
        // 执行分页查询
        Page<User> userPage = userMapper.selectPage(page, wrapper);
        System.out.println("总记录数 = " + userPage.getTotal());
        System.out.println("总页数 = " + userPage.getPages());
        System.out.println("当前页码 = " + userPage.getCurrent());
        // 获取分页查询结果
        List<User> records = userPage.getRecords();
        records.forEach(System.out::println);
    }

9. 代码生成器

  • MyBatis Plus 提供了代码生成器,可以根据数据库表自动生成实体类、Mapper 接口、Service 类和 Controller 类。
public class Generator {

    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8",
                        "root", "")
                .globalConfig(builder -> builder
                        .author("aa")
                        .outputDir("your-path")
                )
                .packageConfig(builder -> builder
                        .parent("your-package")
                        .entity("entity")
                        .mapper("mapper")
                        .service("service")
                        .serviceImpl("service.impl")
                        .xml("mapper.xml")
                )
                .strategyConfig(builder -> builder
                        .entityBuilder()
                        .enableLombok()
                )
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }
}

4.3 高级功能

1. 自动填充

通过在实体类里面的字段进行设置时间戳, create_time, update_time等字段需要自动填充

	@TableField(fill = FieldFill.INSERT) // 插入时自动填充
	private long createTime;
	@TableField(fill = FieldFill.INSERT_UPDATE) // 插入/更新时自动填充
	private long updateTime;

自定义类 MetaObjectHandler 实现 MetaObjectHandler接口,并且去重写其两个方法:

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("开始插入填充...");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("开始更新填充...");
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

2. 乐观锁(并发的情况)

乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。MyBatis-Plus 提供了 OptimisticLockerInnerInterceptor 插件,使得在应用中实现乐观锁变得简单。

乐观锁的实现原理包括以下步骤:

  1. 读取记录时,获取当前的版本号(version)。
  2. 在更新记录时,将这个版本号一同传递。
  3. 执行更新操作时,设置 version = newVersion 的条件为 version = oldVersion。
  4. 如果版本号不匹配,则更新失败。

配置乐观锁插件:

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

Entity对象version字段加上@version注解

import com.baomidou.mybatisplus.annotation.Version;

@Data
public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    private String email;
    @Version
    private Integer version;
}

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

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

相关文章

RCL谐振电压增益曲线

谐振电路如何通过调频实现稳压&#xff1f; 为什么要做谐振&#xff1f; 在谐振状态实现ZVS导通&#xff0c;小电流关断 电压增益GVo/Vin&#xff0c;相当于产出投入比 当ff0时&#xff0c;G1时&#xff0c;输出电压输入电压 当G<1时&#xff0c;输出电压<输入电压 …

JavaScript:表单及正则表达式验证

今天我要介绍的是在JavaScript中关于表单验证内容的知识点介绍&#xff1a; 关于表单验证&#xff0c;我接下来则直接将内容以及效果显示出来并作注解&#xff0c;这样可以清晰看见这个表达验证的妙用&#xff1a; <form id"ff" action"https://www.baidu.…

一、Appium环境安装

找了一圈操作手机的工具或软件&#xff0c;踩了好多坑&#xff0c;最后决定用这个工具(影刀RPA手机用的也是这个)&#xff0c;目前最新的版本是v2.17.1&#xff0c;是基于nodejs环境的&#xff0c;有两种方式&#xff0c;我只试了第一种方式&#xff0c;第二种方式应该是比较简…

【c++深入系列】:new和delete运算符详解

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; “生活不会向你许诺什么&#xff0c;尤其不会向你许诺成功。它只会给你挣扎、痛苦和煎熬的过程。但只要你坚持下去&#xff0c;终有一天&…

正弦波有效值和平均值(学习笔记)

一个周期的正弦波在坐标轴上围的面积有多大&#xff1f; 一般正弦波以 y Asin(wx)表示&#xff0c;其中A为振幅&#xff0c;W为角速度。周期T 2π/w; 确定积分区间是x 0&#xff0c;到x 2π。 计算绝对值积分&#xff1a; 变量代还&#xff1a;wx θ&#xff0c;dx dθ…

第八天 开始Unity Shader的学习之Blinn-Phong光照模型

Unity Shader的学习笔记 第八天 开始Unity Shader的学习之Blinn-Phong光照模型 文章目录 Unity Shader的学习笔记前言一、Blinn-Phong光照模型①计算高光反射部分效果展示 二、召唤神龙:使用Unity内置的函数总结 前言 今天我们编写另一种高光反射的实现方法 – Blinn光照模型…

豆瓣图书数据采集与可视化分析

文章目录 一、适用题目二、豆瓣图书数据采集1. 图书分类采集2. 爬取不同分类的图书数据3. 各个分类数据整合 三、豆瓣图书数据清洗四、数据分析五、数据可视化1. 数据可视化大屏展示 源码获取看下方名片 一、适用题目 基于Python的豆瓣图书数据采集与分析基于Python的豆瓣图书…

常见的爬虫算法

1.base64加密 base64是什么 Base64编码&#xff0c;是由64个字符组成编码集&#xff1a;26个大写字母AZ&#xff0c;26个小写字母az&#xff0c;10个数字0~9&#xff0c;符号“”与符号“/”。Base64编码的基本思路是将原始数据的三个字节拆分转化为四个字节&#xff0c;然后…

YOLOV8 OBB 海思3516训练流程

YOLOV8 OBB 海思3516训练流程 目录 1、 下载带GPU版本的torch(可选) 1 2、 安装 ultralytics 2 3、 下载pycharm 社区版 2 4、安装pycharm 3 5、新建pycharm 工程 3 6、 添加conda 环境 4 7、 训练代码 5 9、配置Ymal 文件 6 10、修改网络结构 9 11、运行train.py 开始训练模…

[MySQL] 事务管理(二) 事务的隔离性底层

事务的隔离性底层 1.数据库并发的场景2.读-写2.1MVCC三个变量2.1.1 3个记录隐藏列字段2.1.2 undo日志 模拟MVCCselect 的读取2.1.3 Read View&#xff08;读视图&#xff09; 3.RR与RC的区别 1.数据库并发的场景 读-读&#xff1a;不存在问题&#xff0c;也不需要并发控制读-写…

20、.NET SDK概述

.NET SDK&#xff08;Software Development Kit&#xff09; 是微软提供的一套开发工具包&#xff0c;用于构建、运行和管理基于 .NET 平台的应用程序。它包含了一组丰富的工具、库和运行时环境&#xff0c;支持开发者在多种操作系统&#xff08;如 Windows、Linux 和 macOS&am…

18-21源码剖析——Mybatis整体架构设计、核心组件调用关系、源码环境搭建

学习视频资料来源&#xff1a;https://www.bilibili.com/video/BV1R14y1W7yS 文章目录 1. 架构设计2. 核心组件及调用关系3. 源码环境搭建3.1 测试类3.2 实体类3.3 核心配置文件3.4 映射配置文件3.5 遇到的问题 1. 架构设计 Mybatis整体架构分为4层&#xff1a; 接口层&#…

冒泡排序、插入排序、快速排序、堆排序、希尔排序、归并排序

目录 冒泡排序插入排序快速排序(未优化版本)快速排序(优化版本)堆排序希尔排序归并排序各排序时间消耗对比 冒泡排序 冒泡排序核心逻辑就是对数组从第一个位置开始进行遍历&#xff0c;如果发现该元素比下一个元素大&#xff0c;则交换位置&#xff0c;如果不大&#xff0c;就…

CD27.【C++ Dev】类和对象 (18)友元和内部类

目录 1.友元 友元函数 几个特点 友元类 格式 代码示例 2.内部类(了解即可) 计算有内部类的类的大小 分析 注意:内部类不能直接定义 内部类是外部类的友元类 3.练习 承接CD21.【C Dev】类和对象(12) 流插入运算符的重载文章 1.友元 友元函数 在CD21.【C Dev】类和…

QT安装详细步骤

下载 清华源 &#xff1a; 清华源 1. 2. 3. 4.

Unity游戏多语言工具包

由于一开始的代码没有考虑多语言场景&#xff0c;导致代码中提示框和UI显示直接用了中文&#xff0c;最近开始提取代码的中文&#xff0c;提取起来太麻烦&#xff0c;所以拓展了之前的多语言包&#xff0c;降低了操作复杂度。最后把工具代码提取出来到单独项目里面&#xff0c;…

实验三 I/O地址译码

一、实验目的 掌握I/O地址译码电路的工作原理。 二、实验电路 实验电路如图1所示&#xff0c;其中74LS74为D触发器&#xff0c;可直接使用实验台上数字电路实验区的D触发器&#xff0c;74LS138为地址译码器&#xff0c; Y0&#xff1a;280H&#xff5e;287H&…

视觉语言导航(VLN):连接语言、视觉与行动的桥梁

文章目录 1. 引言&#xff1a;什么是VLN及其重要性&#xff1f;2. VLN问题定义3. 核心挑战4. 基石&#xff1a;关键数据集与模拟器5. 评估指标6. 主要方法与技术演进6.1 前CLIP时代&#xff1a;奠定基础6.2 后CLIP时代&#xff1a;视觉与语言的统一 7. 最新进展与前沿趋势 (202…

计算机网络中科大 - 第7章 网络安全(详细解析)-以及案例

目录 &#x1f6e1;️ 第8章&#xff1a;网络安全&#xff08;Network Security&#xff09;优化整合笔记&#x1f4cc; 本章学习目标 一、网络安全概念二、加密技术&#xff08;Encryption&#xff09;1. 对称加密&#xff08;Symmetric Key&#xff09;2. 公钥加密&#xff0…

XCTF-web(四)

unserialize3 需要反序列化一下&#xff1a;O:4:“xctf”:2:{s:4:“flag”;s:3:“111”;} php_rce 题目提示rce漏洞&#xff0c;测试一下&#xff1a;?s/Index/\think\app/invokefunction&functioncall_user_func_array&vars[0]phpinfo&vars[1][]1 flag&#xff1…