云计算的服务模式主要包括以下三种:
IaaS(基础设施即服务):提供虚拟化的计算资源,如虚拟机、存储和网络。
PaaS(平台即服务):提供开发和部署应用的平台,包括操作系统、数据库和开发工具。
SaaS(软件即服务):提供通过互联网访问的应用程序,用户无需管理底层基础设施。
以下选项通常不属于云计算的服务模式:
DaaS(数据即服务):虽然DaaS是一种云服务,但它通常被视为SaaS或PaaS的一部分,而不是独立的云计算服务模式。
On-premises(本地部署):这是传统的本地部署模式,不属于云计算的范畴。
因此,On-premises(本地部署) 不属于云计算的服务模式。
在SQL中,GROUP BY 和 HAVING 是用于对查询结果进行分组和过滤的关键字。它们通常与聚合函数(如 COUNT、SUM、AVG、MAX、MIN 等)一起使用。
GROUP BY 用于将结果集按一个或多个列进行分组,通常与聚合函数一起使用,以便对每个组进行计算。
1 | SELECT column1, aggregate_function(column2) |
假设有一个 orders 表,包含以下数据:
| order_id | customer_id | amount |
|---|---|---|
| 1 | 101 | 100 |
| 2 | 102 | 200 |
| 3 | 101 | 150 |
| 4 | 103 | 300 |
如果我们想计算每个客户的订单总金额,可以使用 GROUP BY:
1 | SELECT customer_id, SUM(amount) AS total_amount |
| customer_id | total_amount |
|---|---|
| 101 | 250 |
| 102 | 200 |
| 103 | 300 |
HAVING 用于过滤分组后的结果集。它与 WHERE 类似,但 WHERE 是在分组前过滤行,而 HAVING 是在分组后过滤组。
1 | SELECT column1, aggregate_function(column2) |
继续使用上面的 orders 表,如果我们只想查询订单总金额大于 200 的客户,可以使用 HAVING:
1 | SELECT customer_id, SUM(amount) AS total_amount |
| customer_id | total_amount |
|---|---|
| 101 | 250 |
| 103 | 300 |
GROUP BY 和 HAVING 通常一起使用,以便对分组后的结果进行过滤。
假设我们想查询订单数量大于 1 的客户:
1 | SELECT customer_id, COUNT(order_id) AS order_count |
| customer_id | order_count |
|---|---|
| 101 | 2 |
GROUP BY:用于按列分组,通常与聚合函数一起使用。
HAVING:用于过滤分组后的结果集,条件中通常包含聚合函数。
WHERE 和 HAVING 的区别:
WHERE 在分组前过滤行。HAVING 在分组后过滤组。通过合理使用 GROUP BY 和 HAVING,可以对数据进行更复杂的分析和查询。
分布式数据库的可扩展性通常包括以下几个方面:
水平扩展(Horizontal Scaling):
垂直扩展(Vertical Scaling):
数据分片(Sharding):
负载均衡(Load Balancing):
高可用性(High Availability):
弹性扩展(Elastic Scaling):
分布式数据库的可扩展性不包括以下方面:
单点性能优化(Single-node Performance Optimization):
数据一致性(Data Consistency):
安全性(Security):
事务管理(Transaction Management):
分布式数据库的可扩展性主要关注系统的扩展能力,包括水平扩展、垂直扩展、数据分片、负载均衡、高可用性和弹性扩展。而单点性能优化、数据一致性、安全性和事务管理虽然重要,但不属于可扩展性的范畴。
主键(Primary Key)是数据库设计中的一个重要概念,它与数据库范式(Normalization)密切相关。主键本身并不直接对应某个范式,但它是实现数据库范式的基础,尤其是在满足第一范式(1NF)和第二范式(2NF)时起到关键作用。
唯一标识:主键用于唯一标识表中的每一行数据。
非空:主键列的值不能为 NULL。
不可重复:主键列的值必须唯一。
要求:表中的每一列都是不可再分的原子值,且每一行都是唯一的。
主键的作用:主键是实现第一范式的关键,因为它确保了每一行的唯一性。
示例:
| 学生ID | 学生姓名 | 课程 |
|---|---|---|
| 1 | 张三 | 数学, 英语 |
| 2 | 李四 | 物理 |
| 学生ID | 学生姓名 | 课程 |
|---|---|---|
| 1 | 张三 | 数学 |
| 1 | 张三 | 英语 |
| 2 | 李四 | 物理 |
要求:在满足 1NF 的基础上,所有非主键列必须完全依赖于主键,而不是部分依赖。
主键的作用:如果表中有复合主键(由多个列组成),则需要确保非主键列完全依赖于整个主键,而不是部分主键。
示例:
| 学生ID | 课程ID | 学生姓名 | 课程名称 |
|---|---|---|---|
| 1 | 101 | 张三 | 数学 |
| 1 | 102 | 张三 | 英语 |
| 2 | 101 | 李四 | 数学 |
学生姓名 只依赖于 学生ID,而 课程名称 只依赖于 课程ID,存在部分依赖。| 学生ID | 学生姓名 |
|---|---|
| 1 | 张三 |
| 2 | 李四 |
| 课程ID | 课程名称 |
|---|---|
| 101 | 数学 |
| 102 | 英语 |
| 学生ID | 课程ID |
|---|---|
| 1 | 101 |
| 1 | 102 |
| 2 | 101 |
要求:在满足 2NF 的基础上,所有非主键列之间不能有传递依赖。
主键的作用:主键仍然是确保数据唯一性和依赖关系的基础。
示例:
| 学生ID | 学生姓名 | 学院ID | 学院名称 |
|---|---|---|---|
| 1 | 张三 | 101 | 理学院 |
| 2 | 李四 | 102 | 文学院 |
学院名称 依赖于 学院ID,而 学院ID 依赖于 学生ID,存在传递依赖。| 学生ID | 学生姓名 | 学院ID |
|---|---|---|
| 1 | 张三 | 101 |
| 2 | 李四 | 102 |
| 学院ID | 学院名称 |
|---|---|
| 101 | 理学院 |
| 102 | 文学院 |
主键本身并不直接对应某个范式,但它是实现数据库范式的基础。
第一范式(1NF):主键确保每一行的唯一性。
第二范式(2NF):主键(尤其是复合主键)确保非主键列完全依赖于整个主键。
第三范式(3NF):主键确保非主键列之间没有传递依赖。
因此,主键是数据库设计中的核心概念,贯穿于数据库范式的各个阶段。
在 MySQL 和 Oracle 数据库中,事务传播行为并不是数据库本身的特性,而是由应用程序框架(如 Spring)或应用程序逻辑控制的。数据库本身只提供事务的基本支持(如 BEGIN、COMMIT、ROLLBACK),而事务的传播行为是由应用程序框架定义的。
MySQL:
autocommit),即每条 SQL 语句都会自动作为一个事务提交。BEGIN 或 START TRANSACTION 显式开启事务,然后通过 COMMIT 或 ROLLBACK 结束事务。Oracle:
AUTOCOMMIT 为 OFF 来禁用自动提交。BEGIN 或 START TRANSACTION 开启事务,通过 COMMIT 或 ROLLBACK 结束事务。事务传播行为是由应用程序框架(如 Spring)定义的,常见的传播行为包括:
REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。
REQUIRES_NEW:总是创建一个新事务,如果当前存在事务,则挂起当前事务。
SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
其他传播行为(如 MANDATORY、NOT_SUPPORTED、NEVER、NESTED)。
Spring 框架:
REQUIRED。1 | // 默认传播行为是 REQUIRED |
MySQL 和 Oracle 数据库:
BEGIN、COMMIT、ROLLBACK)。MySQL 和 Oracle 数据库:
BEGIN、COMMIT、ROLLBACK)。autocommit)。事务传播行为:
REQUIRED。如果需要使用特定的事务传播行为,需要在应用程序框架中配置,而不是在数据库中配置。
在数据库事务的隔离级别中,读未提交(Read Uncommitted) 是最低的事务隔离级别。它允许一个事务读取另一个事务尚未提交的数据,可能会导致脏读(Dirty Read)、不可重复读(Non-Repeatable Read) 和 幻读(Phantom Read) 等问题。
定义:一个事务可以读取另一个事务未提交的数据。
问题:
假设有两个事务:事务A 和 事务B。
事务A:
1 | BEGIN; |
事务B:
1 | BEGIN; |
如果 事务A 回滚,事务B 读取到的数据(balance = 100)就是无效的,这就是脏读。
在 MySQL 和 Oracle 中,可以通过以下方式设置事务的隔离级别为 读未提交。
1 | SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; |
Oracle 不支持 读未提交 隔离级别,最低的隔离级别是 读已提交(Read Committed)。
对数据一致性要求不高的场景:
性能要求较高的场景:
| 隔离级别 | 脏读(Dirty Read) | 不可重复读(Non-Repeatable Read) | 幻读(Phantom Read) |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能 |
| 串行化 | 不可能 | 不可能 | 不可能 |
读未提交(Read Uncommitted) 是最低的事务隔离级别,允许读取未提交的数据。
它会导致脏读、不可重复读 和 幻读 等问题。
适用于对数据一致性要求不高、但对性能要求较高的场景。
在 MySQL 中可以通过 SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 设置,但 Oracle 不支持该隔离级别。
fetch 和 pull 是 Git 中常用的两个命令,它们的作用和用法有所不同,但都与从远程仓库获取代码有关。以下是它们的详细说明和用法:
git fetchgit fetch 用于从远程仓库下载最新的提交记录和分支信息,但不会自动合并到本地分支。
从远程仓库获取最新的提交记录和分支信息。
更新本地仓库的远程跟踪分支(如 origin/main),但不会修改本地分支(如 main)。
1 | git fetch <远程仓库名> |
<远程仓库名>:通常是 origin,表示远程仓库的别名。
获取远程仓库的所有更新:
1 | git fetch origin |
获取特定分支的更新:
1 | git fetch origin main |
查看远程分支的更新:
1 | git fetch --all |
远程仓库的最新提交会被下载到本地,但不会影响本地分支。
可以使用 git log origin/main 查看远程分支的更新。
git pullgit pull 用于从远程仓库下载最新的提交记录并自动合并到当前本地分支。
从远程仓库获取最新的提交记录。
自动将远程分支的更新合并到当前本地分支。
1 | git pull <远程仓库名> <远程分支名> |
<远程仓库名>:通常是 origin。
<远程分支名>:要拉取的远程分支名称。
拉取远程仓库的 main 分支并合并到当前分支:
1 | git pull origin main |
拉取远程仓库的当前分支(默认):
1 | git pull |
远程仓库的最新提交会被下载并自动合并到当前本地分支。
如果存在冲突,需要手动解决冲突后再提交。
fetch 和 pull 的区别| 特性 | git fetch |
git pull |
|---|---|---|
| 作用 | 下载远程仓库的最新提交记录和分支信息 | 下载远程仓库的最新提交记录并合并到本地分支 |
| 是否修改本地分支 | 不会修改本地分支 | 会自动合并到当前本地分支 |
| 使用场景 | 查看远程仓库的更新 | 获取远程仓库的更新并合并到本地 |
| 冲突处理 | 不会产生冲突 | 可能会产生冲突,需要手动解决 |
fetch 的工作流程:获取远程仓库的更新:
1 | git fetch origin |
查看远程分支的更新:
1 | git log origin/main |
手动合并远程分支到本地分支:
1 | git merge origin/main |
pull 的工作流程:直接拉取并合并远程分支:
1 | git pull origin main |
如果有冲突,解决冲突后提交:
1 | git add . |
git fetch:
git pull:
根据具体需求选择合适的命令:
如果需要查看远程仓库的更新而不修改本地分支,使用 git fetch。
如果需要快速同步远程仓库的更新并合并到本地分支,使用 git pull。
要实现一个忽略正负、按绝对值大小排序的数组,可以使用编程语言提供的排序函数,并在排序时比较元素的绝对值。以下是使用 Java 的实现示例:
Java 可以使用 Arrays.sort() 方法,并通过自定义比较器来实现按绝对值排序。
1 | import java.util.Arrays; |
Arrays.sort(arr, new Comparator<Integer>() { ... }):对数组 arr 进行排序,排序规则由自定义的 Comparator 定义。
Integer.compare(Math.abs(a), Math.abs(b)):比较两个整数的绝对值。
核心思想:在排序时比较元素的绝对值。
实现方式:
Arrays.sort() 和自定义 Comparator。以上实现均能正确地将数组按绝对值大小排序,同时保留原始元素的正负符号。
MySQL 支持不同的事务隔离级别,控制事务间的可见性:
READ UNCOMMITTED:最低级别,允许读取未提交的数据。
READ COMMITTED:只能读取已提交的数据。
REPEATABLE READ(默认):确保事务内多次读取结果一致。
SERIALIZABLE:最高级别,完全隔离,事务串行执行。
TRUNCATE 是 SQL 中的一种数据操作语言(DML,Data Manipulation Language)命令,用于快速删除表中的所有数据。与 DELETE 命令不同,TRUNCATE 不会逐行删除数据,而是直接释放表的数据页,因此效率更高。
TRUNCATE 的特点删除所有数据:TRUNCATE 会删除表中的所有数据,但保留表结构(如表定义、索引、约束等)。
不可回滚:TRUNCATE 操作通常不可回滚(取决于数据库的实现,某些数据库支持事务中的 TRUNCATE 回滚)。
不触发触发器:TRUNCATE 不会触发 DELETE 触发器。
重置自增值:对于带有自增列(如 AUTO_INCREMENT 或 IDENTITY)的表,TRUNCATE 会重置自增值。
高效:TRUNCATE 直接释放数据页,比 DELETE 更快。
TRUNCATE 的语法1 | TRUNCATE TABLE 表名; |
1 | TRUNCATE TABLE employees; |
删除 employees 表中的所有数据,但保留表结构。
TRUNCATE 与 DELETE 的区别| 特性 | TRUNCATE |
DELETE |
|---|---|---|
| 删除方式 | 直接释放数据页,删除所有数据 | 逐行删除数据 |
| 效率 | 高效 | 较低 |
| 可回滚 | 通常不可回滚(取决于数据库实现) | 可回滚 |
| 触发触发器 | 不触发 | 触发 |
| 重置自增值 | 重置 | 不重置 |
| 条件删除 | 不支持 | 支持(通过 WHERE 子句) |
TRUNCATE 的适用场景需要快速清空表中的所有数据。
不需要逐行删除数据。
不需要触发 DELETE 触发器。
需要重置自增列的值。
权限要求:TRUNCATE 通常需要较高的权限(如 DROP 权限)。
不可恢复:TRUNCATE 操作通常不可恢复,执行前需谨慎。
外键约束:某些数据库(如 MySQL 的 InnoDB)在有外键约束时可能不允许 TRUNCATE。
TRUNCATE 是一种高效的删除操作,用于快速清空表中的所有数据。
它属于 DML(数据操作语言),但与 DELETE 相比,TRUNCATE 更高效且不触发触发器。
适用于需要快速清空表数据的场景,但需注意其不可回滚的特性。
聚簇索引(Clustered Index) 和 非聚簇索引(Non-Clustered Index) 是数据库中两种重要的索引类型,它们在存储方式和查询性能上有显著区别。以下是它们的详细对比和常见问题:
聚簇索引决定了表中数据的物理存储顺序。
一个表只能有一个聚簇索引,因为数据只能按一种方式物理排序。
数据与索引一起存储:聚簇索引的叶子节点直接存储数据行。
物理排序:表中的数据按聚簇索引的键值顺序存储。
高效查询:对于范围查询和排序操作,聚簇索引非常高效。
主键默认是聚簇索引:在大多数数据库(如 MySQL InnoDB)中,主键默认是聚簇索引。
1 | CREATE TABLE employees ( |
id 列是聚簇索引,数据按 id 的顺序存储。
非聚簇索引是一种独立于数据存储的索引结构。
一个表可以有多个非聚簇索引。
索引与数据分离:非聚簇索引的叶子节点存储的是指向数据行的指针(如主键值或行地址)。
逻辑排序:索引按键值排序,但数据不按索引顺序存储。
适合查询单条记录:对于精确查找,非聚簇索引效率较高。
需要额外存储空间:非聚簇索引需要额外的存储空间来存储索引结构。
1 | CREATE INDEX idx_name ON employees(name); -- 非聚簇索引 |
name 列是非聚簇索引,索引按 name 排序,但数据仍按 id 的顺序存储。
| 特性 | 聚簇索引 | 非聚簇索引 |
|---|---|---|
| 数量 | 一个表只能有一个 | 一个表可以有多个 |
| 数据存储 | 索引的叶子节点存储数据行 | 索引的叶子节点存储指向数据行的指针 |
| 物理排序 | 数据按索引键值物理排序 | 数据不按索引键值物理排序 |
| 查询性能 | 范围查询和排序操作高效 | 精确查找操作高效 |
| 存储空间 | 不需要额外存储空间 | 需要额外存储空间 |
| 默认索引 | 主键默认是聚簇索引 | 需要显式创建 |
答案:不可以。一个表只能有一个聚簇索引,因为数据只能按一种方式物理排序。
答案:取决于查询类型:
答案:在大多数数据库(如 MySQL InnoDB)中,主键默认是聚簇索引。但某些数据库(如 SQL Server)允许将非主键列设置为聚簇索引。
答案:
答案:
主键列。
常用于范围查询或排序的列(如日期列)。
常用于查询条件的列(如 WHERE 子句中的列)。
需要创建多个索引的列。
聚簇索引:
非聚簇索引:
根据实际需求合理选择聚簇索引和非聚簇索引,可以显著提高数据库的查询性能。
防止跨域请求(Cross-Origin Request)通常不是目标,因为跨域请求是现代Web应用中常见的需求。相反,目标是安全地处理跨域请求,防止恶意行为。以下是几种常见的方式来管理和控制跨域请求:
CORS 是一种W3C标准,允许服务器明确指定哪些外部域可以访问其资源。
服务器可以通过设置HTTP响应头来控制跨域请求:
Access-Control-Allow-Origin: 指定允许访问资源的域名,* 表示允许所有域名。Access-Control-Allow-Methods: 指定允许的HTTP方法(如GET、POST等)。Access-Control-Allow-Headers: 指定允许的请求头。Access-Control-Allow-Credentials: 指定是否允许发送凭据(如cookies)。通过合理配置CORS,可以限制跨域请求的来源、方法和头信息,防止恶意请求。
JSONP 是一种绕过同源策略的旧方法,通过动态创建<script>标签来加载跨域数据。
由于JSONP存在安全风险(如XSS攻击),现代应用通常不再推荐使用。
使用代理服务器可以避免跨域问题。客户端请求同域下的代理服务器,代理服务器再请求目标服务器并返回结果。
这种方式可以隐藏跨域请求,但需要额外的服务器资源。
浏览器默认遵循同源策略,阻止跨域请求。如果不需要跨域请求,可以依赖浏览器的同源策略来阻止跨域访问。
同源策略限制不同协议、域名或端口之间的请求。
为了防止跨站请求伪造(CSRF)攻击,可以使用CSRF Token。
服务器生成一个唯一的Token,嵌入到表单或请求头中,客户端在请求时携带该Token,服务器验证Token的合法性。
服务器可以通过检查请求头中的Origin或Referer字段,判断请求是否来自允许的域名,拒绝非法来源的请求。
使用HTTPS可以防止中间人攻击,确保跨域请求的安全性。
通过设置CSP头,可以限制页面加载的外部资源(如脚本、样式、图片等),防止恶意跨域请求。
如果需要完全阻止跨域请求,可以依赖浏览器的同源策略或服务器端的限制。但更常见的做法是通过CORS、代理服务器或CSRF Token等方式安全地处理跨域请求,而不是完全阻止。
TRUNCATE 属于 DML(Data Manipulation Language,数据操作语言)。
DML(数据操作语言):用于操作数据库中的数据,常见的命令包括 INSERT、UPDATE、DELETE 和 TRUNCATE。TRUNCATE 用于快速删除表中的所有数据,但保留表结构。
DDL(数据定义语言):用于定义或修改数据库结构,例如 CREATE、ALTER、DROP 等。
DCL(数据控制语言):用于控制数据库访问权限,例如 GRANT、REVOKE 等。
虽然 TRUNCATE 会删除数据,但它不会触发 DELETE 触发器,且通常比 DELETE 更快,因此被归类为 DML 而不是 DDL。
在 MySQL 中,COUNT 和 SUM 是两个常用的聚合函数,分别用于统计行数和计算数值列的总和。以下是它们的具体用法和示例:
COUNT 函数COUNT 函数用于统计符合条件的行数。
1 | COUNT(expression) |
expression 可以是列名、*(表示所有行)或常量。
COUNT(*) 统计所有行,包括 NULL 值。
COUNT(column_name) 统计指定列中非 NULL 值的行数。
统计表中所有行的数量:
1 | SELECT COUNT(*) FROM employees; |
统计某一列中非 NULL 值的行数:
1 | SELECT COUNT(salary) FROM employees; |
结合 WHERE 条件统计:
1 | SELECT COUNT(*) FROM employees WHERE department = 'Sales'; |
统计不同值的数量(结合 DISTINCT):
1 | SELECT COUNT(DISTINCT department) FROM employees; |
SUM 函数SUM 函数用于计算数值列的总和。
1 | SUM(expression) |
expression 必须是数值类型的列或表达式。
SUM 会忽略 NULL 值。
计算某一列的总和:
1 | SELECT SUM(salary) FROM employees; |
结合 WHERE 条件计算总和:
1 | SELECT SUM(salary) FROM employees WHERE department = 'Sales'; |
计算多列的总和:
1 | SELECT SUM(salary + bonus) FROM employees; |
结合 GROUP BY 分组计算总和:
1 | SELECT department, SUM(salary) |
COUNT 和 SUM 的区别| 函数 | 作用 | 适用数据类型 | 是否忽略 NULL |
|---|---|---|---|
COUNT |
统计行数或非 NULL 值的数量 |
任意类型 | 是 |
SUM |
计算数值列的总和 | 数值类型(如整数、浮点数) | 是 |
假设有一个 orders 表,包含以下数据:
| order_id | customer_id | amount |
|---|---|---|
| 1 | 101 | 100 |
| 2 | 102 | 200 |
| 3 | 101 | 150 |
| 4 | 103 | NULL |
统计订单总数:
1 | SELECT COUNT(*) FROM orders; |
结果:4
统计有效订单数量(amount 列非 NULL):
1 | SELECT COUNT(amount) FROM orders; |
结果:3
计算订单总金额:
1 | SELECT SUM(amount) FROM orders; |
结果:450
按客户分组统计订单数量和总金额:
1 | SELECT customer_id, COUNT(*) AS order_count, SUM(amount) AS total_amount |
结果:
| customer_id | order_count | total_amount |
|---|---|---|
| 101 | 2 | 250 |
| 102 | 1 | 200 |
| 103 | 1 | NULL |
通过 COUNT 和 SUM,可以轻松实现数据统计和汇总功能。
在 Spring 中,@Transactional 注解用于声明事务管理。然而,在某些场景下,@Transactional 可能会失效,导致事务无法正常工作。以下是一些常见的失效场景及其原因:
@Transactional 注解应用于非 public 方法Spring 的事务管理默认只对 public 方法生效。如果 @Transactional 注解应用于 private、protected 或 package-visible 方法,事务将不会生效。
1 |
|
确保 @Transactional 注解仅用于 public 方法。
Spring 的事务管理基于 AOP 代理实现。如果在同一个类中,一个非事务方法调用另一个事务方法,事务将不会生效,因为代理对象无法拦截内部调用。
1 | public class UserService { |
将事务方法移到另一个类中。
使用 AopContext.currentProxy() 获取当前代理对象并调用方法:
1 | public void outerMethod() { |
@Transactional 默认只在抛出未检查异常(RuntimeException 及其子类)或错误时回滚事务。如果异常被捕获且未重新抛出,事务将不会回滚。
1 |
|
在捕获异常后重新抛出:
1 |
|
使用 @Transactional(rollbackFor = Exception.class) 指定回滚的异常类型。
@Transactional 的传播行为(propagation)配置不当可能导致事务失效。例如,如果方法配置为 Propagation.NOT_SUPPORTED,则不会开启事务。
1 |
|
根据业务需求正确配置传播行为。
某些数据库引擎(如 MySQL 的 MyISAM)不支持事务。如果使用这些引擎,@Transactional 将失效。
将数据库引擎切换为支持事务的引擎(如 InnoDB)。
@Transactional 注解未启用如果 Spring 的事务管理未启用,@Transactional 注解将不会生效。
确保在配置类或 XML 配置中启用了事务管理:
使用注解驱动:
1 |
|
使用 XML 配置:
1 | <tx:annotation-driven /> |
如果未正确配置事务管理器(如 PlatformTransactionManager),事务将无法正常工作。
确保正确配置事务管理器:
1 |
|
在多线程环境下,事务可能无法跨线程传播,导致事务失效。
1 |
|
避免在多线程环境下直接操作事务,或将事务操作放在主线程中执行。
@Transactional 注解的 rollbackFor 配置错误默认情况下,@Transactional 只在抛出 RuntimeException 时回滚事务。如果抛出了检查异常(checked exception),事务不会回滚。
1 |
|
显式指定 rollbackFor:
1 |
|
如果事务超时(timeout)或只读(readOnly)配置不当,可能导致事务失效。
1 | // 超时时间过短 |
根据业务需求合理配置 timeout 和 readOnly。
@Transactional 失效的常见原因包括:
方法非 public。
方法内部调用。
异常被捕获且未抛出。
传播行为配置不当。
数据库引擎不支持事务。
事务管理未启用。
事务管理器未正确配置。
多线程环境下事务失效。
rollbackFor 配置错误。
事务超时或只读配置不当。
通过排查这些问题,可以有效避免 @Transactional 失效的情况。
在 Java 中,switch-case 语句的条件表达式(即 switch 后的表达式)可以使用以下类型:
byte
short
int
char
1 | int day = 3; |
Byte
Short
Integer
Character
1 | Integer num = 2; |
enum
1 | enum Day { MONDAY, TUESDAY, WEDNESDAY } |
String
1 | String fruit = "Apple"; |
以下类型不能用于 switch-case 语句:
long
float
double
boolean
自定义对象(除非是枚举类型)
1 | long value = 10L; |
case 标签必须是常量:case 后的值必须是编译时常量,不能是变量或表达式。
default 分支可选:default 分支是可选的,用于处理未匹配的情况。
break 语句:如果没有 break,程序会继续执行后续 case 分支(称为“贯穿”行为)。
1 | int num = 2; |
switch-case 语句支持以下类型:
基本数据类型:byte、short、int、char
包装类型:Byte、Short、Integer、Character
枚举类型:enum
字符串类型:String
不支持的类型包括 long、float、double、boolean 以及自定义对象(枚举除外)。
Spring Boot 是 Spring 生态系统中的一个重要项目,旨在简化 Spring 应用的开发和部署。它通过提供默认配置和自动化配置,减少了开发者在搭建和配置 Spring 应用时的复杂性,使开发者能够快速构建独立、生产级的应用程序。
快速启动:通过内嵌的 Tomcat、Jetty 或 Undertow 服务器,Spring Boot 应用可以直接运行,无需部署到外部服务器。
自动配置:Spring Boot 根据项目的依赖自动配置 Spring 框架和第三方库,减少了手动配置的工作量。
独立运行:Spring Boot 应用可以打包为可执行的 JAR 或 WAR 文件,直接通过 java -jar 命令运行。
无代码生成:Spring Boot 不需要生成额外的代码或 XML 配置文件,完全基于注解和约定。
生产就绪:提供健康检查、指标监控、外部化配置等生产级特性。
Spring Boot Starter:一系列依赖描述符,简化 Maven/Gradle 依赖管理。例如:
spring-boot-starter-web:用于构建 Web 应用。spring-boot-starter-data-jpa:用于集成 JPA 和数据库。spring-boot-starter-test:用于单元测试。Spring Boot Auto-Configuration:根据类路径中的依赖自动配置 Spring 应用。
Spring Boot Actuator:提供生产级监控和管理功能,如健康检查、指标收集等。
Spring Boot CLI:命令行工具,支持快速开发和运行 Groovy 脚本。
简化开发:通过默认配置和自动化配置,减少了开发者的工作量。
快速迭代:支持热部署和热加载,开发效率高。
微服务友好:Spring Boot 是构建微服务架构的理想选择,与 Spring Cloud 无缝集成。
丰富的生态系统:与 Spring 生态系统(如 Spring Data、Spring Security 等)完美集成。
社区支持:Spring Boot 拥有庞大的社区和丰富的文档资源。
Web 应用开发:快速构建 RESTful API 或 MVC 应用。
微服务架构:作为微服务的基础框架,与 Spring Cloud 配合使用。
批处理应用:通过 Spring Batch 支持批处理任务。
数据访问应用:集成 JPA、MyBatis 等数据访问框架。
消息驱动应用:集成 RabbitMQ、Kafka 等消息中间件。
添加依赖(pom.xml):
1 | <dependencies> |
编写主类:
1 | import org.springframework.boot.SpringApplication; |
运行应用:
MyApplication 类,Spring Boot 会启动内嵌的 Tomcat 服务器。http://localhost:8080/hello,页面会显示 Hello, Spring Boot!。Spring Boot 支持通过 application.properties 或 application.yml 文件进行配置。
application.properties:
1 | server.port=8081 |
application.yml:
1 | server: |
加载 @SpringBootApplication 注解标记的主类。
扫描类路径下的组件(如 @Component、@Service、@Repository 等)。
根据依赖自动配置 Spring 应用(如数据源、Web 服务器等)。
启动内嵌的 Web 服务器并运行应用。
Spring Cloud:用于构建分布式系统和微服务架构。
Spring Data:简化数据访问操作,支持关系型数据库和 NoSQL 数据库。
Spring Security:提供身份验证和授权功能。
Spring Batch:用于批处理任务。
Spring Integration:支持企业集成模式。
Spring Boot 的版本号格式为 X.Y.Z:
X:主版本号,表示重大更新。
Y:次版本号,表示新特性或改进。
Z:修订版本号,表示 bug 修复。
截至 2023 年,最新的稳定版本是 Spring Boot 3.x,基于 Spring Framework 6.x 和 Java 17+。
Spring Boot 是一个强大的框架,通过简化配置和提供默认值,极大地提高了开发效率。它适用于各种应用场景,从简单的 Web 应用到复杂的微服务架构。无论是初学者还是经验丰富的开发者,Spring Boot 都是一个值得学习和使用的工具。
ArrayList 和 HashMap 是 Java 集合框架中两个常用的类,但它们的设计目的和使用场景完全不同。以下是它们的详细区别:
ArrayList:
List 接口的实现类。HashMap:
Map 接口的实现类。key-value),键不允许重复,值可以重复。ArrayList:
HashMap:
ArrayList:
1 | ArrayList<String> list = new ArrayList<>(); |
HashMap:
1 | HashMap<String, Integer> map = new HashMap<>(); |
ArrayList:
O(1)。O(1)。O(n)(需要移动元素)。HashMap:
O(1)(平均情况)。O(1)(平均情况)。ArrayList:
null)。1 | ArrayList<Object> list = new ArrayList<>(); |
HashMap:
null,值可以为 null)。1 | HashMap<String, Object> map = new HashMap<>(); |
ArrayList:
Collections.synchronizedList 或 CopyOnWriteArrayList。HashMap:
Collections.synchronizedMap 或 ConcurrentHashMap。ArrayList:
1 | ArrayList<Integer> list = new ArrayList<>(); |
HashMap:
1 | HashMap<Integer, String> map = new HashMap<>(); |
ArrayList:
1 | ArrayList<String> list = new ArrayList<>(); |
HashMap:
1 | HashMap<String, Integer> map = new HashMap<>(); |
ArrayList:
HashMap:
| 特性 | ArrayList |
HashMap |
|---|---|---|
| 数据结构 | 动态数组 | 哈希表 |
| 存储方式 | 单个元素 | 键值对 |
| 查询性能 | 按索引访问:O(1) |
按键访问:O(1)(平均情况) |
| 插入/删除性能 | 末尾操作:O(1);中间操作:O(n) |
O(1)(平均情况) |
| 允许重复 | 允许元素重复 | 键不允许重复,值允许重复 |
| 线程安全性 | 非线程安全 | 非线程安全 |
| 适用场景 | 有序集合、按索引访问 | 键值对存储、快速查找 |
根据具体需求选择合适的集合类:如果需要有序集合,使用 ArrayList;如果需要键值对存储,使用 HashMap。