1. 如何实现演示课程计划上移、下移以及排序的功能
- 需求说明: 实现课程计划的排序功能,支持课程条目的上移、下移。
- 实现方式:
- 数据库中需有一个
orderby
字段,表示排序的顺序值。 - 当需要调整顺序时,通过交换
orderby
的值来实现。- 上移: 找到目标条目的上一条数据,交换两条数据的
orderby
值。 - 下移: 找到目标条目的下一条数据,交换两条数据的
orderby
值。
- 上移: 找到目标条目的上一条数据,交换两条数据的
- 可使用事务来保证多条
update
操作的原子性。
- 数据库中需有一个
- 注意点:
- 主键设计应该避免包含业务逻辑,使用单独的排序字段管理顺序。
- 如果涉及批量排序更新,应考虑数据库锁及性能问题。
2. 使用 @Select
查询值时,没查到数据时返回 0
-
代码问题:
@Select("select COALESCE((MAX(orderby),0)) from teachplan where course_id = #{courseId} and parentid = #{parentId}")
COALESCE
的作用: 若MAX(orderby)
查询结果为null
,则返回默认值0
。- 解释: 直接使用
COALESCE
函数处理空值的情况,避免返回null
。 - SQL 结果: 如果符合条件的数据不存在,返回
0
,这是默认值。
3. @RequestPart("filedata") MultipartFile upload
的作用
- 含义:
@RequestPart
是用来绑定multipart/form-data
请求中的某个分段。filedata
是表单文件字段的名称。MultipartFile
是 Spring 提供的接口,用于处理文件上传。
- 用途: 在处理文件上传时,将请求中的文件映射为
MultipartFile
对象。 - 对比:
- 如果使用
@RequestParam
,适合处理简单的表单字段参数。 - 如果需要上传文件,必须使用
@RequestPart
。
- 如果使用
4. 是否需要写 consumes = MediaType.MULTIPART_FORM_DATA_VALUE
- 是否必要:
- 有必要时:
- 明确限制 Controller 方法只能处理
multipart/form-data
请求。 - 提升代码的可读性和语义化。
- 明确限制 Controller 方法只能处理
- 不必要时:
- Spring 默认可以识别文件上传的
Content-Type
,可以省略consumes
。
- Spring 默认可以识别文件上传的
- 有必要时:
- 结论:
- 添加
consumes
可以作为明确约束,但省略也不影响功能。
- 添加
5. @ConfigurationProperty
与 @Value
的区别
-
-
通过
${}
语法手动读取配置文件中的属性值。 -
更适合简单属性注入。
-
示例:
@Value("${minio.endpoint}") private String endpoint;
-
-
-
支持批量绑定属性到一个 POJO。
-
可以使用嵌套结构,更适合复杂配置。
-
示例:
@ConfigurationProperties(prefix = "minio") public class MinioProperties { private String endpoint; // getter & setter }
-
-
对比结论:
- 如果配置项较多,推荐使用
@ConfigurationProperties
。 - 如果只是简单读取一个值,可以直接用
@Value
。
- 如果配置项较多,推荐使用
6. 如何在 Service 层使用配置类中的值
-
方法:
-
依赖注入:
-
直接注入配置类:
@Autowired private MinioConfig minioConfig; private String endpoint = minioConfig.getEndpoint();
-
配置类需要使用
@Component
或@Configuration
注册为 Spring Bean。
-
-
推荐方式:
- 使用依赖注入,而不是通过
new
创建实例,避免失去 Spring 容器的管理。
- 使用依赖注入,而不是通过
-
7. 关于 WebAppBoundary
的作用
-
WebAppBoundary
是什么:- HTTP 协议中的分隔符,用于分割不同的表单字段。
- 通过
boundary
标识每个部分,明确文件内容和其他字段的分界点。
-
是否必要:
-
必要性: 对于多部分表单请求 (
multipart/form-data
),boundary
是必须的。 -
示例:
--WebAppBoundary Content-Disposition: form-data; name="filedata"; filename="1.jpg" Content-Type: application/octet-stream
-
8. 为什么在调用 @Transactional
方法前事务已开启
- 事务机制:
- Spring AOP 会在方法执行前开启事务,并在方法结束后提交事务。
- 即使数据库操作尚未开始,事务已经启动。
- 执行逻辑:
- 调用方法前,事务代理类开启事务。
- 方法执行过程中,数据库操作已在事务范围内。
- 方法结束时提交事务。
- 解释误解:
- 数据库锁住不是在具体操作时,而是事务一开启,锁机制生效。
9. 为什么自调用事务方法时事务不起作用
- 原因:
- Spring 的事务依赖于 AOP 动态代理,代理对象负责开启和管理事务。
- 如果同类内调用方法,直接通过
this
调用,会绕过代理,导致事务不生效。
- 形象解释:
- 你是商店老板,想通过保险公司索赔损失,但直接拿着自己开的证明是不行的,必须通过第三方(代理)办理。
- 解决方法:
- 使用代理对象调用事务方法。
- 或者将事务方法提取到另一个类中。
10. 另一个类调用本类事务方法为什么可以生效
- 原理:
- 另一个类调用时,Spring 使用代理对象调用事务方法,事务生效。
- 本类直接调用是实例对象调用,绕过了代理。
- 总结:
- 事务生效依赖代理对象,必须通过代理对象进行调用。
11. 为什么 1024 \* 1024 \* 1
是 1MB
- 计算方式:
- 1KB = 1024字节。
- 1MB = 1024 * 1024 字节。
1024 * 1024 * 1
表示 1MB。
12. RandomAccessFile
是什么
-
定义:
RandomAccessFile
是 Java 中用于文件读写的类,支持随机访问。- 可以以只读(
r
)或读写(rw
)模式打开文件。
-
示例:
RandomAccessFile file = new RandomAccessFile("sourceFile", "r");
13. raf_write.write(b, 0, len)
的作用
- 代码解析:
b
: 字节数组,存储读取到的数据。0
: 从数组起始位置写入。len
: 写入的字节长度。
14. 为什么还需要 createNewFile()
-
代码疑问:
File file = new File(chunkPath + i); if (file.exists()) { file.delete(); } boolean newFile = file.createNewFile();
-
解释:
new File(...)
只是创建一个文件对象,并未在磁盘上生成文件。createNewFile()
才真正创建文件。