苍穹外卖笔记集锦
Build 建造者模式
- 由
Lombok
提供
实现:
在类上添加 @Builder
注解:
import lombok.Builder;import lombok.Data;@Data@Builderpublic class EmployeeLoginVO { private Long id; private String userName; private String name; private String token;}
使用 Builder
创建对象
EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder() .id(employee.getId()) .userName(employee.getUsername()) .name(employee.getName()) .token(token) .build();
多个pom文件的管理
使用父POM
的 : 只在父级
pom
文件中添加.
如果在子pom
中多次添加,可能会导致不能启动项目的问题.
maven聚合工程
-
聚合器
POM
(父工程)
聚合工程中要指定为
pom
,用于聚合多个模块,并通过锁定各个依赖版本。并在
标签内列出所有的子模块
-
模块间的依赖
如果一个子模块需要依赖另一个子模块,在pom.xml
中添加对该子模块的依赖:
<dependency> <groupId>com.example</groupId> <artifactId>module1</artifactId> <version>1.0-SNAPSHOT</version></dependency>
-
聚合器模块不要作为依赖:
<dependency> <groupId>Warren</groupId> <artifactId>Common</artifactId> <version>0.0.1-SNAPSHOT</version></dependency>
Common
模块是一个POM
类型的聚合器(包含Common-util
与Service-util
)。聚合器模块一般不产生jar
包,作为依赖引用可能会引起问题;如果需要使用,引用聚合器的具体模块(例如Common-util
、Service-util
)即可.
Query参数
-
URL
中?
后面的键值对传递的请求参数。比如/employee/page?name=zhangsan
中的name=zhangsan
就是Query
参数。 -
使用
@RequestParam
注解来获取Query
参数
Pagehelper分页
PageHelper
是由GitHub
上的pagehelper
组织维护的MyBatis
通用分页插件。
-
使用这个方法之后就不用导入
MP
的配置类了 -
PageHelper.startPage(page, pageSize)
:page
表示当前页码,pageSize
是每页显示的记录数。 -
在调用该方法之后,紧跟着执行的
查询
会被拦截,并在生成的SQL
中添加分页相关的语句,注意
后面跟随的方法要返回一个Page
PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize()); Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);//后续定义
需要导入依赖:
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.6</version></dependency>
生成主键
MyBatis
的XML
映射文件中的两个属性
-
useGeneratedKeys=\"true\"
自动生成主键。并在执行插入操作后,返回主键id
-
keyProperty=\"id\"
指定生成的主键值应对应到Java
对象中的哪个属性上。
设置时间自动填充:
-
在数据库表中设置时间自动填充,就不用在实体类上指定自动填充时间了
-
数据库中时间类字段使用
timestamp
,实体类中使用String
.
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT \'创建时间\', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT \'更新时间\',
jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8
设置时间正确显示,如果不加这个配置,时间显示的不是中国时间.
使用AOP实现公共字段填充
实现思路:
-
自定义一个注解
@AutoFill
用于标识需要自动填充公共字段的方法,并指定对应的数据库操作类型(INSERT
或UPDATE
)。 -
定义枚举
OperationType
:枚举数据库操作类型,包括INSERT
和UPDATE
。 -
创建切面类
AutoFillAspect
:在该切面中,拦截标注了@AutoFill
注解的方法,获取方法参数(即实体对象),并通过反射为其公共字段赋值。
具体步骤:
-
定义
@AutoFill
注解:import com.sky.enumeration.OperationType;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface AutoFill { //指定数据库操作类型:INSERT或UPDATE OperationType value();}
-
定义枚举:
package com.sky.enumeration; public enum OperationType { INSERT, UPDATE }
-
创建切面类
AutoFillAspect
:package com.sky.aspect;import com.sky.annotation.AutoFill;import com.sky.constant.AutoFillConstant;import com.sky.context.BaseContext;import com.sky.enumeration.OperationType;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import java.lang.reflect.Method;import java.time.LocalDateTime;/** * 切面类,实现公共字段的自动填充 */@Aspect@Component@Slf4jpublic class AutoFillAspect { /** * 定义切入点,拦截所有在mapper包下,标注了@AutoFill注解的方法 */ @Pointcut(\"execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)\") public void autoFillPointCut() {} /** * 前置通知,在方法执行前进行公共字段的赋值 */ @Before(\"autoFillPointCut()\") public void autoFill(JoinPoint joinPoint) { log.info(\"开始进行公共字段自动填充...\"); // 获取被拦截的方法签名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 获取方法上的@AutoFill注解 AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获取数据库操作类型 OperationType operationType = autoFill.value(); // 获取方法参数,即实体对象 Object[] args = joinPoint.getArgs(); if (args == null || args.length == 0) { return; } Object entity = args[0]; // 获取当前时间和当前用户ID LocalDateTime now = LocalDateTime.now(); Long currentId = BaseContext.getCurrentId(); try { if (operationType == OperationType.INSERT) { // 插入操作,设置创建和更新相关字段 setFieldValue(entity, AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class, now); setFieldValue(entity, AutoFillConstant.SET_CREATE_USER, Long.class, currentId); setFieldValue(entity, AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class, now); setFieldValue(entity, AutoFillConstant.SET_UPDATE_USER, Long.class, currentId); } else if (operationType == OperationType.UPDATE) { // 更新操作,设置更新时间和更新人字段 setFieldValue(entity, AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class, now); setFieldValue(entity, AutoFillConstant.SET_UPDATE_USER, Long.class, currentId); } } catch (Exception e) { log.error(\"公共字段自动填充失败\", e); } } /** * 通过反射为实体对象的指定字段赋值 */ private void setFieldValue(Object entity, String methodName, Class<?> parameterType, Object value) throws Exception { Method method = entity.getClass().getDeclaredMethod(methodName, parameterType); method.invoke(entity, value); }}
说明:
-
@Pointcut
:定义切入点,拦截com.sky.mapper
包下所有标注了@AutoFill
注解的方法。 -
@Before
:在目标方法执行前进行公共字段的赋值操作。 -
setFieldValue
方法:通过反射调用实体对象的setter方法,为公共字段赋值。
-
在
Mapper
方法上使用@AutoFill
注解:package com.sky.mapper;import com.sky.annotation.AutoFill;import com.sky.entity.Employee;import com.sky.enumeration.OperationType;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Update;@Mapperpublic interface EmployeeMapper { /** * 插入员工数据 */ @Insert(\"INSERT INTO employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user, status) \" + \"VALUES (#{name}, #{username}, #{password}, #{phone}, #{sex}, #{idNumber}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser}, #{status})\") @AutoFill(OperationType.INSERT) void insert(Employee employee); /** * 更新员工数据 */ @Update(\"UPDATE employee SET name = #{name}, username = #{username}, password = #{password}, phone = #{phone}, sex = #{sex}, id_number = #{idNumber}, \" + \"update_time =
HttpClient
- 用于在
java
程序中构造并发送http
请求
第三方 HTTP 客户端库 Apache HttpComponents
-
HttpClient
- 提供
HTTP
客户端相关的核心功能,比如创建请求、发送请求、处理响应、连接池管理等。
- 提供
-
HttpCore
- 提供低层次的
HTTP
传输组件和协议处理,HttpClient
库在此之上进行更高级封装。
- 提供低层次的
-
HttpMime
- 用于处理多部分(multipart)请求或文件上传等场景。
引入依赖:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version></dependency>
主要类:
CloseableHttpClient
:
最核心的客户端类,负责创建并执行请求。获取方式:
CloseableHttpClient httpClient = HttpClients.createDefault();
也可以使用 HttpClientBuilder
自定义配置:
CloseableHttpClient httpClient = HttpClientBuilder.create() .setMaxConnTotal(100) // 设置最大连接数 .setMaxConnPerRoute(20) // 每个路由的最大连接数 // ... 其他自定义配置 .build();
请求方法
-
HttpGet
:执行 GET 请求,不带请求体 -
HttpPost
:执行 POST 请求,可带表单或 JSON 等请求体 -
HttpPut
:执行 PUT 请求 -
HttpDelete
:执行 DELETE 请求 -
HttpPatch
:执行 PATCH 请求(需要 4.2+ 版本支持)
/** * 测试通过httpclient发送GET方式的请求 */ @Test public void testGET() throws Exception{ //创建httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建请求对象 HttpGet httpGet = new HttpGet(\"http://localhost:8080/user/shop/status\"); //发送请求,接受响应结果 CloseableHttpResponse response = httpClient.execute(httpGet); //获取服务端返回的状态码 int statusCode = response.getStatusLine().getStatusCode(); System.out.println(\"服务端返回的状态码为:\" + statusCode); HttpEntity entity = response.getEntity(); String body = EntityUtils.toString(entity); System.out.println(\"服务端返回的数据为:\" + body); //关闭资源 response.close(); httpClient.close();}
/** * 测试通过httpclient发送POST方式的请求 */ @Test public void testPOST() throws Exception{ // 创建httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); //创建请求对象 HttpPost httpPost = new HttpPost(\"http://localhost:8080/admin/employee/login\"); JSONObject jsonObject = new JSONObject(); jsonObject.put(\"username\",\"admin\"); jsonObject.put(\"password\",\"123456\"); StringEntity entity = new StringEntity(jsonObject.toString()); //指定请求编码方式 entity.setContentEncoding(\"utf-8\"); //数据格式 entity.setContentType(\"application/json\"); httpPost.setEntity(entity); //发送请求 CloseableHttpResponse response = httpClient.execute(httpPost); //解析返回结果 int statusCode = response.getStatusLine().getStatusCode(); System.out.println(\"响应码为:\" + statusCode); HttpEntity entity1 = response.getEntity(); String body = EntityUtils.toString(entity1); System.out.println(\"响应数据为:\" + body); //关闭资源 response.close(); httpClient.close(); }
文件上传(HttpMime)
如果要上传文件,可以使用 HttpMime
提供的 MultipartEntityBuilder
:
MultipartEntityBuilder builder = MultipartEntityBuilder.create();builder.addTextBody(\"description\", \"测试文件\");builder.addBinaryBody(\"file\", new File(\"test.png\"), ContentType.DEFAULT_BINARY, \"test.png\");HttpEntity multipart = builder.build();HttpPost httpPost = new HttpPost(\"https://example.com/upload\");httpPost.setEntity(multipart);// 执行并处理响应
MyBatis条件查询问题
List<Dish> list(Dish dish);
<select id=\"list\" resultType=\"com.sky.entity.Dish\" parameterType=\"com.sky.entity.Dish\"> select * from dish <where> <if test=\"name != null\"> and name like concat(\'%\',#{name},\'%\') </if> <if test=\"categoryId != null\"> and category_id = #{categoryId} </if> <if test=\"status != null\"> and status = #{status} </if> </where> order by create_time desc</select>
这里虽然只传入了一个dish
参数,但是MyBatis
在 SQL 里会使用 dish
的属性来构造查询条件.
Redis 匹配模式
-
*
匹配任意数量的字符(包括空字符)。
例如:user*
能匹配所有以 “user” 开头的键,如user1
、username
等。
-
?
匹配任意一个单独的字符。
例如:user?
可以匹配user1
、userA
,但不能匹配user12
或user
。
-
[abc]
匹配方括号中任意一个字符。
例如:user[123]
可以匹配user1
、user2
和user3
。
-
[a-z]
通过指定一个字符范围,匹配该范围内的任意字符。
例如:file[0-9]
能匹配file0
到file9
之间的任何一个文件名。
Spring el表达式
使用注解(比如 @Cacheable
、@CacheEvict
、@CachePut
)进行缓存操作时,需要通过SpEL
动态计算缓存键(key)或执行条件(condition )。
key
属性指定生成缓存键的规则condition
属性指定执行缓存操作的条件
@Cacheable( value = \"users\", key = \"#id\", // 使用 SpEL,生成 key condition = \"#id > 0\" // 只有当 id>0 时才执行缓存)public User getUserById(int id) { // ... return user;}
key = \"#id\"
表示缓存键就是方法第一个参数id
的值。- 如果不指定
key
,默认情况下Spring
会将所有方法参数组合起来作为缓存键。
访问方法参数
@Cacheable(value = \"users\", key = \"#p0\") public User findUserById(Long id) { // ...}
#p0
或#a0
都表示第一个参数id
的值。第二个是#p1
或#a1
.
使用方法的返回值做缓存键:
@CachePut(value = \"users\", key = \"#result.id\")public User updateUser(User user) { // 修改用户并返回更新后的 user return user;}
- 这里
#result
是方法的返回值User
,再取id
作为缓存键。
使用 condition
属性来决定是否执行缓存操作:
@Cacheable(value = \"numbers\", key = \"#number\", condition = \"#number > 10\")public int processNumber(int number) { // 只有当 number > 10 时才会执行缓存 return number * 2;}
spring task
- 任务调度工具,按照约定时间自动执行代码逻辑
周的位置表示星期几,日和周一般只出现一个,另一个使用?
表示,因为不能保证某一天的具体是星期几
websocket
- 一次握手实现双向数据传输
apache poi
- 读写excel,word等文件
日期相关
- 指定参数中日期的格式
@DateTimeFormat(pattern = \"yyyy-MM-dd\") LocalDate begin, @DateTimeFormat(pattern = \"yyyy-MM-dd\") LocalDate end
- 获得某一天的起始时间和结束时间,即0时0分0秒和23时59分59秒
LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN); LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
使用map作为sql查询参数
在MyBatis
中传入一个 Map 作为参数时,MyBatis
会将 Map中的键key
视为参数名
Map map = new HashMap();map.put(\"begin\", beginTime);map.put(\"end\", endTime);map.put(\"status\", Orders.COMPLETED);Double turnover = orderMapper.sumByMap(map);
<select id=\"sumByMap\" resultType=\"java.lang.Double\"> select sum(amount) from orders <where> <if test=\"begin != null\"> and order_time > #{begin} </if> <if test=\"end != null\"> and order_time < #{end} </if> <if test=\"status != null\"> and status = #{status} </if> </where> </select>
StringUtils.join()拼接方法
import org.apache.commons.lang3.StringUtils;import java.util.ArrayList;import java.util.List;public class JoinExample { public static void main(String[] args) { // 使用 List 拼接 List<String> list = new ArrayList<>(); list.add(\"张三\"); list.add(\"李四\"); list.add(\"王五\"); String joinedList = StringUtils.join(list, \",\"); System.out.println(\"拼接后的 List:\" + joinedList); // 输出:张三,李四,王五 // 使用数组拼接 String[] arr = {\"苹果\", \"香蕉\", \"橘子\"}; String joinedArray = StringUtils.join(arr, \"-\"); System.out.println(\"拼接后的数组:\" + joinedArray); // 输出:苹果-香蕉-橘子 }}