记录下实习中项目中常用的一些方法
定时任务的使用 --
1、首先,在项目启动类上添加 @EnableScheduling 注解,开启对定时任务的支持。@EnableScheduling 注解的作用是发现注解 @Scheduled 的任务并在后台执行该任务。
2、编写定时任务类和方法,定时任务类通过 Spring IOC 加载,使用 @Component 注解。
3、定时方法使用 @Scheduled注解。
@Scheduled(cron="0 0 2 1 * ? * ") 通过 cron 表达式定义规则。
cron 表达式是一个字符串,该字符串由 6 个空格分为 7 个域,每一个域代表一个时间含义。 通常定义 “年” 的部分可以省略,实际常用的 Cron 表达式由前 6 部分组成。格式如下:
* :表示所有值,可解读为 “每”。 如果在 “日” 这个域中设置 *,表示每一天都会触发。
? :表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如,要在每月的 8 号触发一个操作,但不关心是周几,我们可以这么设置 0 0 0 8 * ?。
CollectionUtils工具类
CollectionUtils在真实项目中,是一个非常好用的工具类,使用非常频繁。它可以使代码更加简洁和安全。
.isEmpty
.isEqualCollection 比较两个集合中的元素是否相同,不会考虑它们的顺序。
.addAll(myList, 1, 2, 3)
.subtract(list1, list2) - 返回从集合 a 中减去集合 b 中元素后的结果。
.isEqualCollection(list1, list2) - 检查两个集合是否包含相同的元素,无论顺序如何。
.union 并集
.intersection 交集
StringUtils工具类
.isEmpty(str) - 检查字符串是否为空或为 null。
.isNotBlank(str) - 检查字符串是否不为空、不为 null,且不只包含空白字符。
StringUtils.equalsIgnoreCaseAndEmpty(a,b) 可以避免空指针问题,例如a = null
*stream流
Stream 是 Java 中用于处理集合数据的流式处理 API,它提供了一种更简洁、高效的方式来操作集合元素。
过滤数据
使用 filter 方法可以从集合中过滤出满足某个条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0) //过滤(筛选)
.collect(Collectors.toList());
// evenNumbers 包含 [2, 4, 6]
映射数据
使用 map 方法可以对集合中的元素进行映射转换。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// nameLengths 包含 [5, 3, 7]
提取部分数据
使用 limit 方法可以限制 Stream 处理的元素数量。
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
List<String> firstTwoFruits = fruits.stream()
.limit(2) // 按排序顺序限制
.collect(Collectors.toList());
// firstTwoFruits 包含 ["apple", "banana"]
跳过部分数据
使用 skip 方法可以跳过前面的一些元素。
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
List<String> skippedFruits = fruits.stream()
.skip(2) // 按排序顺序跳过
.collect(Collectors.toList());
// skippedFruits 包含 ["cherry", "date"]
聚合操作:
使用聚合操作如 reduce 可以对集合进行汇总、计算。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
// sum 等于 15
分组和分区:
使用 groupingBy 和 partitioningBy 方法可以对数据进行分组或分区。
List<Person> people = ... // 一组 Person 对象
// 键值为country, List<Person>为country相同的人员列表
Map<String, List<Person>> peopleByCountry
= people.stream()
.collect(Collectors.groupingBy(Person::getCountry));
// 键值为布尔值,以年龄18为分界线(分为两个区)
Map<Boolean, List<Person>> adultsAndMinors
= people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() >= 18));
例子:
分组方法1
List<Map<String, Object>> distinctList = convertedList.stream()
.collect(Collectors.toMap(
map -> map.get("serviceNo"),
Function.identity(),
(existing, replacement) -> existing
))
.values()
.stream()
.collect(Collectors.toList());
collect(Collectors.toMap(...))用于将Stream中的元素映射为一个新的Map,其中的键是每个Map对象中的"serviceNo"字段的值,而值是原始的Map对象(即Function.identity()表示不做任何转换),并且如果有重复的键,则保留旧值(即(existing, replacement) -> existing表示保留旧值)。
.values()将上一步得到的Map中的所有值(Map的value集合)提取出来,返回一个Collection。
.stream()再将这个Collection转换成一个Stream,以便后续操作。
分组方法2
List<Map<String, Object>> distinctList = convertedList.stream()
.collect(Collectors.groupingBy(map -> map.get("serviceNo")))
.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
在这个示例中,我们首先使用Collectors.groupingBy根据"serviceNo"字段的值将原始列表中的Map对象分组到一个Map中,然后通过values()方法获取分组后的Collection,再通过flatMap将多个分组合并为一个流,最后使用Collectors.toList()将所有的Map对象收集到一个列表中。
这样就可以得到按"serviceNo"字段分组的不同组的Map对象列表,而且不会包含重复的"serviceNo"值。
例子:
Map<Long,List<Long>> purchaseIdToGoodsIds = physicalTestingGoodsPurchases.stream()
.collect(Collectors.groupingBy(PhysicalTestingGoodsPurchase::getPurchaseId,
Collectors.mapping(PhysicalTestingGoodsPurchase::getGoodsId, Collectors.toList())));
列表 physicalTestingGoodsPurcha,这个列表的元素类型是PhysicalTestingGoodsPurchase。根据某个属性(purchaseId)对这个列表进行分组,并收集每个分组中元素的另一个属性(goodsId)到一个列表中。
代码执行的过程如下:
physicalTestingGoodsPurchases.stream():将 physicalTestingGoodsPurchases 列表转化为 Stream。
Collectors.groupingBy(...): 这是一个 Collector,用于按照指定的属性对 Stream 中的元素进行分组。在此例中,使用 PhysicalTestingGoodsPurchase::getPurchaseId 作为键(key),表示根据 purchaseId 进行分组。
Collectors.mapping(...): 这是内部 Collector,用于对每个分组进行进一步的处理。在此,表示对每个分组中的元素,获取它们的 goodsId 属性。
Collectors.toList(): 表示您想将每个分组中的元素(已经是 goodsId 属性的形式了)收集到一个列表中。
最终,得到一个 Map<Long, List<Long>>,其中键是 purchaseId,值是与该 purchaseId 相关的 goodsId 列表。
例如,如果有以下 physicalTestingGoodsPurchases 列表:
[
{purchaseId: 1, goodsId: 10},
{purchaseId: 1, goodsId: 11},
{purchaseId: 2, goodsId: 20}
]
经过上述代码处理后,您将得到以下的映射:
{
1: [10, 11],
2: [20]
}
例子:
BigDecimal totalInvoiceAmount = distinctList.stream()
.distinct()
.map(map -> (BigDecimal) map.get("billAmount"))
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
.distinct()方法用于去除Stream中的重复元素,确保每个元素都是唯一的。
例子:
BigDecimal invoiceAmount
= omsOrderGoodsRepository.findAllByDeletedFalseAndOrderId(orderIds.get(i))
.stream()
.map(OmsOrderGoods::getReturnAmount) //获取return_amount
.filter(Objects::nonNull) // 过滤掉为null的元素
.reduce(orderAmounts.get(i),BigDecimal::subtract);//逐个减去
SQL
去重
使用DISTINCT关键字: DISTINCT关键字用于从查询结果中去除重复的行。
SELECT DISTINCT column1, column2 FROM your_table;
使用GROUP BY子句: GROUP BY子句通常用于分组聚合操作,但它也可以用于去重数据。您可以将要去重的列放在GROUP BY子句中,并且在SELECT中只选择那些列,这将自动去除重复的行。
SELECT column1, column2 FROM your_table GROUP BY column1, column2;
模糊查询
<if test="returnsNo != null and returnsNo != ''">
AND oor.returns_no LIKE CONCAT('%', #{returnsNo}, '%')
</if>
日期范围查询
<if test="startDate != null and endDate != null">
AND DATE(oor.created_date) BETWEEN #{startDate} AND #{endDate}
</if>
分组后,组内字符拼接
GROUP_CONCAT(g.`name` ORDER BY g.`name` ASC SEPARATOR ', ')
GROUP_CONCAT:这是一个聚合函数,通常与 GROUP BY 子句一起使用。它用于将分组内的多个值合并成一个字符串。
g.name:这是要合并的字段,通常是一个文本字段(字符串)。在这个例子中,它是 g 表中的 name 字段。
ORDER BY g.name ASC:这部分表示在合并这些值之前,按照 name 字段的升序(ASC 表示升序)对它们进行排序。
SEPARATOR ', ':这是用于分隔合并后的值的字符串。在这个例子中,合并后的值之间将使用逗号和空格作为分隔符。
CASE 表达式
CASE
WHEN EXISTS (
SELECT 1
FROM physics_chemistry_test_goods pctg
WHERE pcs.id = pctg.physics_chemistry_test_id
AND (pctg.status = 0 OR pctg.status IS NULL)
) THEN 0
ELSE 1
END AS status
CASE:这是一个 SQL 关键字,表示开始一个条件表达式,用于根据条件返回不同的值。
WHEN EXISTS (...) THEN 0:这是 CASE 表达式的第一个分支。它检查一个子查询是否返回了结果。如果子查询返回了至少一行记录,那么条件成立,这里的返回值是 0。
EXISTS (...):这是一个条件谓词,用于检查子查询是否返回了结果。
SELECT 1
FROM physics_chemistry_test_goods pctg
WHERE pcs.id = pctg.physics_chemistry_test_id
AND (pctg.status = 0 OR pctg.status IS NULL):
这是一个子查询,它从名为 physics_chemistry_test_goods 的表 pctg 中选择 1。该子查询与外部查询的表 pcs 进行了关联,通过 pcs.id = pctg.physics_chemistry_test_id 进行连接。子查询中的条件 (pctg.status = 0 OR pctg.status IS NULL) 用于筛选符合条件的记录。
THEN 0:如果子查询返回结果,那么 CASE 表达式将返回 0。
ELSE 1:如果子查询不返回结果,那么将会执行 ELSE 部分,这里的返回值是 1。
END:END 标志着 CASE 表达式的结束。
AS status:这是一个别名,它将 CASE 表达式的结果列命名为 status。
综合起来,这段代码的作用是根据条件判断子查询是否返回了符合条件的记录,如果有则返回 0,否则返回 1,最后将结果列命名为 status。
备用值
IFNULL(gs.customer_price, fpg.customer_price) AS customerPrice
IFNULL:这是一个 SQL 函数,用于判断第一个参数是否为 NULL,如果是 NULL,则返回第二个参数的值。如果第一个参数不是 NULL,则返回第一个参数的值。
gs.customer_price:这是第一个参数,它表示要检查是否为 NULL 的字段,通常是一个数值或者可以为 NULL 的字段。
fpg.customer_price:这是第二个参数,如果第一个参数 gs.customer_price 是 NULL,则将返回这个字段的值。
AS customerPrice:这是一个别名,它将 IFNULL 函数的结果列命名为 customerPrice,这样在查询结果中就可以使用 customerPrice 来引用这个值。
综合起来,这段代码的作用是检查 gs.customer_price 是否为 NULL,如果是 NULL,则返回 fpg.customer_price 的值,否则返回 gs.customer_price 的值,并将结果列命名为 customerPrice。这在处理具有备用值的情况下非常有用,以确保结果不包含 NULL 值。
待更新