苍穹外卖笔记集锦
Build 建造者模式
- 由
Lombok
提供
实现:
在类上添加 @Builder
注解:
import lombok.Builder;
import lombok.Data;@Data
@Builder
public 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
的 <dependencyManagement>
: 只在父级pom
文件中添加.
如果在子pom
中多次添加,可能会导致不能启动项目的问题.
maven聚合工程
-
聚合器
POM
(父工程)
聚合工程中要指定<packaging>
为pom
,用于聚合多个模块,并通过<dependencyManagement>
锁定各个依赖版本。并在<modules>
标签内列出所有的子模块 -
模块间的依赖
如果一个子模块需要依赖另一个子模块,在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或UPDATEOperationType 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 @Slf4j public 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];// 获取当前时间和当前用户IDLocalDateTime 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;@Mapper public 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方式的请求*/@Testpublic 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方式的请求*/@Testpublic 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,生成 keycondition = "#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) {// 修改用户并返回更新后的 userreturn 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); // 输出:苹果-香蕉-橘子}
}