1
0

feat: 优化日志显示

This commit is contained in:
2026-01-27 10:06:57 +08:00
parent 18cd1dbed8
commit e9b0e79d48

View File

@@ -89,7 +89,8 @@ public class TestApplication extends AbstractTestApplication {
}
private void testQuery() {
formatLog("准备 Specification 查询的测试数据");
// ==================== 测试阶段 1: 准备测试数据 ====================
formatLog("阶段 1: 准备测试数据");
var company1 = companyRepository.save(Company.builder().name("TechCorp").members(100).build());
var company2 = companyRepository.save(Company.builder().name("DataInc").members(50).build());
var company3 = companyRepository.save(Company.builder().name("CloudSys").members(150).build());
@@ -155,7 +156,11 @@ public class TestApplication extends AbstractTestApplication {
.connections(Map.of(Employee.ConnectionType.EMAIL, "alicesmith@example.com"))
.build());
formatLog("1. 基本比较操作符查询 JPA");
// ==================== 测试阶段 2: 基本比较操作符查询 ====================
formatLog("阶段 2: 基本比较操作符查询");
log.info("查询条件: 姓名='Bob' AND 角色!=ADMIN AND 年龄>20 AND 年龄<30 AND 薪资>=40000 AND 薪资<=45000");
formatLog("2.1 JPA Specification");
// 查找姓名为"Bob"、角色不是ADMIN、年龄在20-30之间、薪资在40000-45000之间的员工
var result1_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
cb.equal(root.get(Employee_.name), "Bob"),
@@ -165,9 +170,10 @@ public class TestApplication extends AbstractTestApplication {
cb.greaterThanOrEqualTo(root.get(Employee_.salary), new BigDecimal("40000.00")),
cb.lessThanOrEqualTo(root.get(Employee_.salary), new BigDecimal("45000.00"))
));
Assert.isTrue(result1_jpa.size() == 1, "基本比较操作符查询失败 %d".formatted(result1_jpa.size()));
Assert.isTrue(result1_jpa.size() == 1, "JPA Specification 查询失败 %d".formatted(result1_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result1_jpa.size());
formatLog("1. 基本比较操作符查询 Fenix");
formatLog("2.2 Fenix");
var result1_fenix = employeeRepository.findAll(
builder -> builder.andEquals(Employee.Fields.name, "Bob")
.andNotEquals(Employee.Fields.role, Employee.Role.ADMIN)
@@ -177,9 +183,10 @@ public class TestApplication extends AbstractTestApplication {
.andLessThanEqual(Employee.Fields.salary, new BigDecimal("45000.00"))
.build()
);
Assert.isTrue(result1_fenix.size() == 1, "基本比较操作符查询失败 %d".formatted(result1_fenix.size()));
Assert.isTrue(result1_fenix.size() == 1, "Fenix 查询失败 %d".formatted(result1_fenix.size()));
log.info("Fenix 结果: {} 条记录", result1_fenix.size());
formatLog("1. 基本比较操作符查询 QueryDSL");
formatLog("2.3 QueryDSL");
var result1_querydsl = employeeRepository.findAll(
QEmployee.employee.name.eq("Bob")
.and(QEmployee.employee.role.ne(Employee.Role.ADMIN))
@@ -188,9 +195,10 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.salary.goe(new BigDecimal("40000.00")))
.and(QEmployee.employee.salary.loe(new BigDecimal("45000.00")))
);
Assert.isTrue(result1_querydsl.size() == 1, "基本比较操作符查询失败 %d".formatted(result1_querydsl.size()));
Assert.isTrue(result1_querydsl.size() == 1, "QueryDSL 查询失败 %d".formatted(result1_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result1_querydsl.size());
formatLog("1. 基本比较操作符查询 HQL");
formatLog("2.4 HQL");
var result1_hql = manager.createQuery(
"""
from Employee employee
@@ -203,9 +211,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result1_hql.size() == 1, "基本比较操作符查询失败 %d".formatted(result1_hql.size()));
Assert.isTrue(result1_hql.size() == 1, "HQL 查询失败 %d".formatted(result1_hql.size()));
log.info("HQL 结果: {} 条记录", result1_hql.size());
formatLog("2. 区间和集合操作符查询 JPA");
// ==================== 测试阶段 3: 区间和集合操作符查询 ====================
formatLog("阶段 3: 区间和集合操作符查询");
log.info("查询条件: 年龄 BETWEEN 25-30 AND NOT BETWEEN 40-50 AND 年龄 IN (25,30,35) AND 角色 IN (USER,ADMIN) AND 姓名 NOT IN (Charlie,David)");
formatLog("3.1 JPA Specification");
// 查找年龄在25-30之间、年龄不在40-50之间、年龄在25/30/35中、角色是USER或ADMIN、姓名不在Charlie/David中的员工
var result2_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
cb.between(root.get(Employee_.age), 25, 30),
@@ -215,9 +228,10 @@ public class TestApplication extends AbstractTestApplication {
cb.not(root.get(Employee_.name).in("Charlie", "David")),
root.get(Employee_.name).in("Charlie", "David").not()
));
Assert.isTrue(result2_jpa.size() == 2, "区间和集合操作符查询失败 %d".formatted(result2_jpa.size()));
Assert.isTrue(result2_jpa.size() == 2, "JPA Specification 查询失败 %d".formatted(result2_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result2_jpa.size());
formatLog("2. 区间和集合操作符查询 Fenix");
formatLog("3.2 Fenix");
var result2_fenix = employeeRepository.findAll(
builder -> builder.andBetween(Employee.Fields.age, 25, 30)
.andNotBetween(Employee.Fields.age, 40, 50)
@@ -226,9 +240,10 @@ public class TestApplication extends AbstractTestApplication {
.andNotIn(Employee.Fields.name, List.of("Charlie", "David"))
.build()
);
Assert.isTrue(result2_fenix.size() == 2, "区间和集合操作符查询失败 %d".formatted(result2_fenix.size()));
Assert.isTrue(result2_fenix.size() == 2, "Fenix 查询失败 %d".formatted(result2_fenix.size()));
log.info("Fenix 结果: {} 条记录", result2_fenix.size());
formatLog("2. 区间和集合操作符查询 QueryDSL");
formatLog("3.3 QueryDSL");
var result2_querydsl = employeeRepository.findAll(
QEmployee.employee.age.between(25, 30)
.and(QEmployee.employee.age.between(40, 50).not())
@@ -236,9 +251,10 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.role.in(Employee.Role.USER, Employee.Role.ADMIN))
.and(QEmployee.employee.name.in(List.of("Charlie", "David")).not())
);
Assert.isTrue(result2_querydsl.size() == 2, "区间和集合操作符查询失败 %d".formatted(result2_querydsl.size()));
Assert.isTrue(result2_querydsl.size() == 2, "QueryDSL 查询失败 %d".formatted(result2_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result2_querydsl.size());
formatLog("2. 区间和集合操作符查询 HQL");
formatLog("3.4 HQL");
var result2_hql = manager.createQuery(
"""
from Employee employee
@@ -250,9 +266,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result2_hql.size() == 2, "区间和集合操作符查询失败 %d".formatted(result2_hql.size()));
Assert.isTrue(result2_hql.size() == 2, "HQL 查询失败 %d".formatted(result2_hql.size()));
log.info("HQL 结果: {} 条记录", result2_hql.size());
formatLog("3. 字符串操作符查询 JPA");
// ==================== 测试阶段 4: 字符串操作符查询 ====================
formatLog("阶段 4: 字符串操作符查询");
log.info("查询条件: 姓名 LIKE 'A%' AND NOT LIKE 'C%' AND LOWER(姓名) LIKE '%ali%' AND UPPER(姓名) LIKE '%ALI%' AND LENGTH(姓名)>4 AND LENGTH(姓名)<=10 AND SUBSTRING(姓名,1,3)='Ali'");
formatLog("4.1 JPA Specification");
// 查找以A开头、不以C开头、包含"ali"忽略大小写、名称长度在4-10之间、前3个字符为"Ali"的员工
var result3_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
cb.like(root.get(Employee_.name), "A%"),
@@ -263,9 +284,10 @@ public class TestApplication extends AbstractTestApplication {
cb.lessThanOrEqualTo(cb.length(root.get(Employee_.name)), 10),
cb.equal(cb.substring(root.get(Employee_.name), 0, 3), "Ali")
));
Assert.isTrue(result3_jpa.size() == 1, "字符串操作符查询失败 %d".formatted(result3_jpa.size()));
Assert.isTrue(result3_jpa.size() == 1, "JPA Specification 查询失败 %d".formatted(result3_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result3_jpa.size());
formatLog("3. 字符串操作符查询 Fenix");
formatLog("4.2 Fenix");
log.info("""
Fenix框架当前版本不支持以下字符串操作符:
- cb.length() - 字符串长度函数
@@ -277,9 +299,9 @@ public class TestApplication extends AbstractTestApplication {
.andNotStartsWith(Employee.Fields.name, "C")
.build()
);
log.info("Fenix查询结果: {} 条记录(仅支持部分条件)", result3_fenix.size());
log.info("Fenix 结果: {} 条记录(仅支持部分条件)", result3_fenix.size());
formatLog("3. 字符串操作符查询 QueryDSL");
formatLog("4.3 QueryDSL");
var result3_querydsl = employeeRepository.findAll(
QEmployee.employee.name.startsWith("A")
.and(QEmployee.employee.name.startsWith("C").not())
@@ -289,9 +311,10 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.name.length().loe(10))
.and(QEmployee.employee.name.substring(0, 3).eq("Ali"))
);
Assert.isTrue(result3_querydsl.size() == 1, "字符串操作符查询失败 %d".formatted(result3_querydsl.size()));
Assert.isTrue(result3_querydsl.size() == 1, "QueryDSL 查询失败 %d".formatted(result3_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result3_querydsl.size());
formatLog("3. 字符串操作符查询 HQL");
formatLog("4.4 HQL");
var result3_hql = manager.createQuery(
"""
from Employee employee
@@ -305,35 +328,43 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result3_hql.size() == 1, "字符串操作符查询失败 %d".formatted(result3_hql.size()));
Assert.isTrue(result3_hql.size() == 1, "HQL 查询失败 %d".formatted(result3_hql.size()));
log.info("HQL 结果: {} 条记录", result3_hql.size());
formatLog("4. NULL 和布尔操作符查询 JPA");
// ==================== 测试阶段 5: NULL 和布尔操作符查询 ====================
formatLog("阶段 5: NULL 和布尔操作符查询");
log.info("查询条件: active=true AND bonus IS NOT NULL AND code NOT IN ('E999')");
formatLog("5.1 JPA Specification");
// 查找激活状态为true、bonus不为null、code不在E999中的员工
var result4_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
cb.isTrue(root.get(Employee_.active)),
cb.isNotNull(root.get(Employee_.bonus)),
cb.not(root.get(Employee_.code).in("E999"))
));
Assert.isTrue(!result4_jpa.isEmpty(), "NULL 和布尔操作符查询失败 %d".formatted(result4_jpa.size()));
Assert.isTrue(!result4_jpa.isEmpty(), "JPA Specification 查询失败 %d".formatted(result4_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result4_jpa.size());
formatLog("4. NULL 和布尔操作符查询 Fenix");
formatLog("5.2 Fenix");
var result4_fenix = employeeRepository.findAll(
builder -> builder.andEquals(Employee.Fields.active, true)
.andIsNotNull(Employee.Fields.bonus)
.andNotIn(Employee.Fields.code, List.of("E999"))
.build()
);
Assert.isTrue(!result4_fenix.isEmpty(), "NULL 和布尔操作符查询失败 %d".formatted(result4_fenix.size()));
Assert.isTrue(!result4_fenix.isEmpty(), "Fenix 查询失败 %d".formatted(result4_fenix.size()));
log.info("Fenix 结果: {} 条记录", result4_fenix.size());
formatLog("4. NULL 和布尔操作符查询 QueryDSL");
formatLog("5.3 QueryDSL");
var result4_querydsl = employeeRepository.findAll(
QEmployee.employee.active.isTrue()
.and(QEmployee.employee.bonus.isNotNull())
.and(QEmployee.employee.code.in("E999").not())
);
Assert.isTrue(!result4_querydsl.isEmpty(), "NULL 和布尔操作符查询失败 %d".formatted(result4_querydsl.size()));
Assert.isTrue(!result4_querydsl.isEmpty(), "QueryDSL 查询失败 %d".formatted(result4_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result4_querydsl.size());
formatLog("4. NULL 和布尔操作符查询 HQL");
formatLog("5.4 HQL");
var result4_hql = manager.createQuery(
"""
from Employee employee
@@ -343,9 +374,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(!result4_hql.isEmpty(), "NULL 和布尔操作符查询失败 %d".formatted(result4_hql.size()));
Assert.isTrue(!result4_hql.isEmpty(), "HQL 查询失败 %d".formatted(result4_hql.size()));
log.info("HQL 结果: {} 条记录", result4_hql.size());
formatLog("5. 集合操作符查询 JPA");
// ==================== 测试阶段 6: 集合操作符查询 ====================
formatLog("阶段 6: 集合操作符查询");
log.info("查询条件: skills IS NOT EMPTY AND hobbies IS NOT EMPTY AND 'Reading' MEMBER OF hobbies AND 'Riding' NOT MEMBER OF hobbies AND SIZE(hobbies)>1 AND SIZE(skills)<4");
formatLog("6.1 JPA Specification");
// 查找技能集合非空、爱好集合非空、包含"Reading"爱好、不包含"Riding"爱好、爱好数量大于1、技能数量小于4的员工
var result5_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
cb.isNotEmpty(root.get(Employee_.skills)),
@@ -355,9 +391,10 @@ public class TestApplication extends AbstractTestApplication {
cb.greaterThan(cb.size(root.get(Employee_.hobbies)), 1),
cb.lessThan(cb.size(root.get(Employee_.skills)), 4)
));
Assert.isTrue(result5_jpa.size() == 2, "集合操作符查询失败 %d".formatted(result5_jpa.size()));
Assert.isTrue(result5_jpa.size() == 2, "JPA Specification 查询失败 %d".formatted(result5_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result5_jpa.size());
formatLog("5. 集合操作符查询 Fenix");
formatLog("6.2 Fenix");
log.info("""
Fenix框架当前版本不支持以下集合操作符:
- cb.isNotEmpty() / cb.isEmpty() - 集合非空/空判断
@@ -365,7 +402,7 @@ public class TestApplication extends AbstractTestApplication {
- cb.size() - 集合大小函数
这些集合操作在JPA Criteria中需要复杂的join处理Fenix当前不支持""");
formatLog("5. 集合操作符查询 QueryDSL");
formatLog("6.3 QueryDSL");
var result5_querydsl = employeeRepository.findAll(
QEmployee.employee.skills.isNotEmpty()
.and(QEmployee.employee.hobbies.isNotEmpty())
@@ -374,9 +411,10 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.hobbies.size().gt(1))
.and(QEmployee.employee.skills.size().lt(4))
);
Assert.isTrue(result5_querydsl.size() == 2, "集合操作符查询失败 %d".formatted(result5_querydsl.size()));
Assert.isTrue(result5_querydsl.size() == 2, "QueryDSL 查询失败 %d".formatted(result5_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result5_querydsl.size());
formatLog("5. 集合操作符查询 HQL");
formatLog("6.4 HQL");
var result5_hql = manager.createQuery(
"""
from Employee employee
@@ -389,9 +427,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result5_hql.size() == 2, "集合操作符查询失败 %d".formatted(result5_hql.size()));
Assert.isTrue(result5_hql.size() == 2, "HQL 查询失败 %d".formatted(result5_hql.size()));
log.info("HQL 结果: {} 条记录", result5_hql.size());
formatLog("6. 逻辑操作符查询 JPA");
// ==================== 测试阶段 7: 逻辑操作符查询 ====================
formatLog("阶段 7: 逻辑操作符查询");
log.info("查询条件: (姓名='Alice' OR 姓名='Bob') AND NOT (姓名='Charlie' OR 姓名='David')");
formatLog("7.1 JPA Specification");
// 查找姓名为Alice或Bob、且姓名不为Charlie或David的员工
var result6_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
cb.or(
@@ -405,9 +448,10 @@ public class TestApplication extends AbstractTestApplication {
)
)
));
Assert.isTrue(result6_jpa.size() == 2, "逻辑操作符查询失败 %d".formatted(result6_jpa.size()));
Assert.isTrue(result6_jpa.size() == 2, "JPA Specification 查询失败 %d".formatted(result6_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result6_jpa.size());
formatLog("6. 逻辑操作符查询 Fenix");
formatLog("7.2 Fenix");
log.info("""
Fenix框架当前版本不支持复杂的嵌套OR和NOT组合逻辑
Fenix支持简单的orEquals但不支持嵌套的or + not组合""");
@@ -417,16 +461,18 @@ public class TestApplication extends AbstractTestApplication {
.andNotIn(Employee.Fields.name, List.of("Charlie", "David"))
.build()
);
Assert.isTrue(result6_fenix.size() == 3, "逻辑操作符查询失败 %d".formatted(result6_fenix.size()));
Assert.isTrue(result6_fenix.size() == 3, "Fenix 查询失败 %d".formatted(result6_fenix.size()));
log.info("Fenix 结果: {} 条记录(逻辑不完全等价)", result6_fenix.size());
formatLog("6. 逻辑操作符查询 QueryDSL");
formatLog("7.3 QueryDSL");
var result6_querydsl = employeeRepository.findAll(
QEmployee.employee.name.eq("Alice").or(QEmployee.employee.name.eq("Bob"))
.and(QEmployee.employee.name.eq("Charlie").or(QEmployee.employee.name.eq("David")).not())
);
Assert.isTrue(result6_querydsl.size() == 2, "逻辑操作符查询失败 %d".formatted(result6_querydsl.size()));
Assert.isTrue(result6_querydsl.size() == 2, "QueryDSL 查询失败 %d".formatted(result6_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result6_querydsl.size());
formatLog("6. 逻辑操作符查询 HQL");
formatLog("7.4 HQL");
var result6_hql = manager.createQuery(
"""
from Employee employee
@@ -435,9 +481,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result6_hql.size() == 2, "逻辑操作符查询失败 %d".formatted(result6_hql.size()));
Assert.isTrue(result6_hql.size() == 2, "HQL 查询失败 %d".formatted(result6_hql.size()));
log.info("HQL 结果: {} 条记录", result6_hql.size());
formatLog("7. Specification 链式调用查询 JPA");
// ==================== 测试阶段 8: Specification 链式调用查询 ====================
formatLog("阶段 8: Specification 链式调用查询");
log.info("查询条件: active=true AND age>25 AND role!=ADMIN OR name='Charlie' AND name!='Alice Smith'");
formatLog("8.1 JPA Specification");
// 链式组合激活状态为true、年龄大于25、角色不是ADMIN、或姓名为Charlie、且姓名不为Alice Smith
var result7_jpa = employeeRepository.findAll(
Specification.<Employee>where((root, query, cb) -> cb.isTrue(root.get(Employee_.active)))
@@ -446,9 +497,10 @@ public class TestApplication extends AbstractTestApplication {
.or((root, query, cb) -> cb.equal(root.get(Employee_.name), "Charlie"))
.and((root, query, cb) -> cb.notEqual(root.get(Employee_.name), "Alice Smith"))
);
Assert.isTrue(result7_jpa.size() == 2, "Specification 链式调用失败 %d".formatted(result7_jpa.size()));
Assert.isTrue(result7_jpa.size() == 2, "JPA Specification 链式调用失败 %d".formatted(result7_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result7_jpa.size());
formatLog("7. Specification 链式调用查询 Fenix");
formatLog("8.2 Fenix");
var result7_fenix = employeeRepository.findAll(
builder -> builder.andEquals(Employee.Fields.active, true)
.andGreaterThan(Employee.Fields.age, 25)
@@ -457,9 +509,10 @@ public class TestApplication extends AbstractTestApplication {
.andNotEquals(Employee.Fields.name, "Alice Smith")
.build()
);
Assert.isTrue(result7_fenix.size() == 2, "Specification 链式调用失败 %d".formatted(result7_fenix.size()));
Assert.isTrue(result7_fenix.size() == 2, "Fenix 链式调用失败 %d".formatted(result7_fenix.size()));
log.info("Fenix 结果: {} 条记录", result7_fenix.size());
formatLog("7. Specification 链式调用查询 QueryDSL");
formatLog("8.3 QueryDSL");
var result7_querydsl = employeeRepository.findAll(
QEmployee.employee.active.isTrue()
.and(QEmployee.employee.age.gt(25))
@@ -467,9 +520,10 @@ public class TestApplication extends AbstractTestApplication {
.or(QEmployee.employee.name.eq("Charlie"))
.and(QEmployee.employee.name.ne("Alice Smith"))
);
Assert.isTrue(result7_querydsl.size() == 2, "Specification 链式调用失败 %d".formatted(result7_querydsl.size()));
Assert.isTrue(result7_querydsl.size() == 2, "QueryDSL 链式调用失败 %d".formatted(result7_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result7_querydsl.size());
formatLog("7. Specification 链式调用查询 HQL");
formatLog("8.4 HQL");
var result7_hql = manager.createQuery(
"""
from Employee employee
@@ -481,9 +535,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result7_hql.size() == 2, "Specification 链式调用失败 %d".formatted(result7_hql.size()));
Assert.isTrue(result7_hql.size() == 2, "HQL 链式调用失败 %d".formatted(result7_hql.size()));
log.info("HQL 结果: {} 条记录", result7_hql.size());
formatLog("8. Join 操作查询 JPA");
// ==================== 测试阶段 9: Join 操作查询 ====================
formatLog("阶段 9: Join 操作查询");
log.info("查询条件: company.name='TechCorp' AND company.name!='DataInc' AND skill.name='Java' AND skill.name!='MySQL' AND prop.value='Senior' AND prop.value!='Junior'");
formatLog("9.1 JPA Specification");
// 查找公司名为TechCorp、技能包含Java、属性值为Senior的员工使用join、fetch、集合join、map join
var result8_jpa = employeeRepository.findAll((root, query, cb) -> {
return cb.and(
@@ -498,9 +557,10 @@ public class TestApplication extends AbstractTestApplication {
cb.notEqual(root.join(Employee_.properties).value(), "Junior")
);
});
Assert.isTrue(result8_jpa.size() == 1, "Join 操作查询失败 %d".formatted(result8_jpa.size()));
Assert.isTrue(result8_jpa.size() == 1, "JPA Specification Join 查询失败 %d".formatted(result8_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result8_jpa.size());
formatLog("8. Join 操作查询 Fenix");
formatLog("9.2 Fenix");
log.info("""
Fenix框架当前版本不支持显式的join操作:
- root.join() - 显式关联查询
@@ -511,7 +571,7 @@ public class TestApplication extends AbstractTestApplication {
注意由于类型系统的限制doAny中使用join可能会有类型推断问题
建议对于join等复杂查询直接使用JPA Specification原生方式""");
formatLog("8. Join 操作查询 QueryDSL");
formatLog("9.3 QueryDSL");
var result8_querydsl = employeeRepository.findAll(
QEmployee.employee.company().name.eq("TechCorp")
.and(QEmployee.employee.company().name.ne("DataInc"))
@@ -520,9 +580,10 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.properties.containsValue("Senior"))
.and(QEmployee.employee.properties.containsValue("Junior").not())
);
Assert.isTrue(result8_querydsl.size() == 1, "Join 操作查询失败 %d".formatted(result8_querydsl.size()));
Assert.isTrue(result8_querydsl.size() == 1, "QueryDSL Join 查询失败 %d".formatted(result8_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result8_querydsl.size());
formatLog("8. Join 操作查询 HQL");
formatLog("9.4 HQL");
var result8_hql = manager.createQuery(
"""
from Employee employee
@@ -538,9 +599,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result8_hql.size() == 1, "Join 操作查询失败 %d".formatted(result8_hql.size()));
Assert.isTrue(result8_hql.size() == 1, "HQL Join 查询失败 %d".formatted(result8_hql.size()));
log.info("HQL 结果: {} 条记录", result8_hql.size());
formatLog("9. 子查询和聚合函数查询 JPA");
// ==================== 测试阶段 10: 子查询和聚合函数查询 ====================
formatLog("阶段 10: 子查询和聚合函数查询");
log.info("查询条件: salary>AVG(salary) AND 5<>COUNT(id) AND salary+COALESCE(bonus,0)>55000 AND salary+COALESCE(bonus,0)<70000 AND active=true AND name!='David' AND age>28 AND role!=USER");
formatLog("10.1 JPA Specification");
// 查找薪资高于平均薪资、总记录数不为5、总薪酬在55000-70000之间、激活状态为true、姓名不为David、年龄大于28、角色不是USER的员工
var result9_jpa = employeeRepository.findAll((root, query, cb) -> {
var avgSalarySubquery = query.subquery(Double.class);
@@ -566,9 +632,10 @@ public class TestApplication extends AbstractTestApplication {
cb.notEqual(root.get(Employee_.role), Employee.Role.USER)
);
});
Assert.isTrue(result9_jpa.isEmpty(), "子查询(聚合函数)+ 数学运算失败 %d".formatted(result9_jpa.size()));
Assert.isTrue(result9_jpa.isEmpty(), "JPA Specification 子查询(聚合函数)+ 数学运算失败 %d".formatted(result9_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result9_jpa.size());
formatLog("9. 子查询和聚合函数查询 Fenix");
formatLog("10.2 Fenix");
log.info("""
Fenix框架当前版本不支持以下高级查询特性:
- query.subquery() - 子查询
@@ -578,7 +645,7 @@ public class TestApplication extends AbstractTestApplication {
这些是SQL级别的复杂查询Fenix主要用于动态条件构建
可以通过doAny使用原生CriteriaBuilder实现部分聚合操作""");
formatLog("9. 子查询和聚合函数查询 QueryDSL");
formatLog("10.3 QueryDSL");
var avgQuery = factory.select(QEmployee.employee.salary.avg());
var countQuery = factory.select(QEmployee.employee.count());
var result9_querydsl = employeeRepository.findAll(
@@ -591,9 +658,10 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.age.gt(28))
.and(QEmployee.employee.role.ne(Employee.Role.USER))
);
Assert.isTrue(result9_querydsl.isEmpty(), "子查询(聚合函数)+ 数学运算失败 %d".formatted(result9_querydsl.size()));
Assert.isTrue(result9_querydsl.isEmpty(), "QueryDSL 子查询(聚合函数)+ 数学运算失败 %d".formatted(result9_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result9_querydsl.size());
formatLog("9. 子查询和聚合函数查询 HQL");
formatLog("10.4 HQL");
var result9_hql = manager.createQuery(
"""
from Employee employee
@@ -608,9 +676,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result9_hql.isEmpty(), "子查询(聚合函数)+ 数学运算失败 %d".formatted(result9_hql.size()));
Assert.isTrue(result9_hql.isEmpty(), "HQL 子查询(聚合函数)+ 数学运算失败 %d".formatted(result9_hql.size()));
log.info("HQL 结果: {} 条记录", result9_hql.size());
formatLog("10. 排序查询 JPA");
// ==================== 测试阶段 11: 排序查询 ====================
formatLog("阶段 11: 排序查询");
log.info("查询条件: active=true AND role!=ADMIN AND age>20 AND salary<60000 ORDER BY age DESC, name ASC");
formatLog("11.1 JPA Specification");
// 查找激活状态为true、角色不是ADMIN、年龄大于20、薪资小于60000的员工按年龄降序、姓名升序排序
var result10_jpa = employeeRepository.findAll(
(root, query, cb) -> cb.and(
@@ -624,9 +697,10 @@ public class TestApplication extends AbstractTestApplication {
Sort.Order.asc(Employee_.NAME)
)
);
Assert.isTrue(result10_jpa.size() == 3, "排序查询失败 %d".formatted(result10_jpa.size()));
Assert.isTrue(result10_jpa.size() == 3, "JPA Specification 排序查询失败 %d".formatted(result10_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result10_jpa.size());
formatLog("10. 排序查询 Fenix");
formatLog("11.2 Fenix");
log.info("""
Fenix框架使用Spring Data JPA原生的Sort对象进行排序
Fenix构建查询条件Sort对象通过repository.findAll()的第二个参数传入""");
@@ -641,9 +715,10 @@ public class TestApplication extends AbstractTestApplication {
Sort.Order.asc(Employee.Fields.name)
)
);
Assert.isTrue(result10_fenix.size() == 3, "排序查询失败 %d".formatted(result10_fenix.size()));
Assert.isTrue(result10_fenix.size() == 3, "Fenix 排序查询失败 %d".formatted(result10_fenix.size()));
log.info("Fenix 结果: {} 条记录", result10_fenix.size());
formatLog("10. 排序查询 QueryDSL");
formatLog("11.3 QueryDSL");
var result10_querydsl = employeeRepository.findAll(
QEmployee.employee.active.isTrue()
.and(QEmployee.employee.role.ne(Employee.Role.ADMIN))
@@ -652,9 +727,10 @@ public class TestApplication extends AbstractTestApplication {
QEmployee.employee.age.desc(),
QEmployee.employee.name.asc()
);
Assert.isTrue(result10_querydsl.size() == 3, "排序查询失败 %d".formatted(result10_querydsl.size()));
Assert.isTrue(result10_querydsl.size() == 3, "QueryDSL 排序查询失败 %d".formatted(result10_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result10_querydsl.size());
formatLog("10. 排序查询 HQL");
formatLog("11.4 HQL");
var result10_hql = manager.createQuery(
"""
from Employee employee
@@ -666,9 +742,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result10_hql.size() == 3, "排序查询失败 %d".formatted(result10_hql.size()));
Assert.isTrue(result10_hql.size() == 3, "HQL 排序查询失败 %d".formatted(result10_hql.size()));
log.info("HQL 结果: {} 条记录", result10_hql.size());
formatLog("11. 分页查询 JPA");
// ==================== 测试阶段 12: 分页查询 ====================
formatLog("阶段 12: 分页查询");
log.info("查询条件: active=true AND role!=ADMIN AND age>20 AND salary<60000 ORDER BY age LIMIT 2 OFFSET 0");
formatLog("12.1 JPA Specification");
// 分页查找激活状态为true、角色不是ADMIN、年龄大于20、薪资小于60000的员工每页2条按年龄排序
var page11_jpa = employeeRepository.findAll(
(root, query, cb) -> cb.and(
@@ -679,10 +760,11 @@ public class TestApplication extends AbstractTestApplication {
),
PageRequest.of(0, 2, Sort.by(Employee_.AGE))
);
Assert.isTrue(page11_jpa.getContent().size() == 2, "分页大小不正确 %d".formatted(page11_jpa.getContent().size()));
Assert.isTrue(page11_jpa.getTotalElements() == 3, "总元素数不正确 %d".formatted(page11_jpa.getTotalElements()));
Assert.isTrue(page11_jpa.getContent().size() == 2, "JPA Specification 分页大小不正确 %d".formatted(page11_jpa.getContent().size()));
Assert.isTrue(page11_jpa.getTotalElements() == 3, "JPA Specification 总元素数不正确 %d".formatted(page11_jpa.getTotalElements()));
log.info("JPA Specification 结果: {} 条记录,总记录数: {}", page11_jpa.getContent().size(), page11_jpa.getTotalElements());
formatLog("11. 分页查询 Fenix");
formatLog("12.2 Fenix");
log.info("""
Fenix框架使用Spring Data JPA原生的Pageable对象进行分页
Fenix构建查询条件Pageable对象通过repository.findAll()的第二个参数传入""");
@@ -694,10 +776,11 @@ public class TestApplication extends AbstractTestApplication {
.build(),
PageRequest.of(0, 2, Sort.by(Employee.Fields.age))
);
Assert.isTrue(page11_fenix.getContent().size() == 2, "分页大小不正确 %d".formatted(page11_fenix.getContent().size()));
Assert.isTrue(page11_fenix.getTotalElements() == 3, "总元素数不正确 %d".formatted(page11_fenix.getTotalElements()));
Assert.isTrue(page11_fenix.getContent().size() == 2, "Fenix 分页大小不正确 %d".formatted(page11_fenix.getContent().size()));
Assert.isTrue(page11_fenix.getTotalElements() == 3, "Fenix 总元素数不正确 %d".formatted(page11_fenix.getTotalElements()));
log.info("Fenix 结果: {} 条记录,总记录数: {}", page11_fenix.getContent().size(), page11_fenix.getTotalElements());
formatLog("11. 分页查询 QueryDSL");
formatLog("12.3 QueryDSL");
log.info("""
QueryDSL支持分页查询:
- offset() - 跳过记录数
@@ -710,10 +793,11 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.salary.lt(new BigDecimal("60000.00"))),
PageRequest.of(0, 2, Sort.by(QEmployee.employee.age.getMetadata().getName()))
);
Assert.isTrue(page11_querydsl.getContent().size() == 2, "分页大小不正确 %d".formatted(page11_querydsl.getContent().size()));
Assert.isTrue(page11_querydsl.getTotalElements() == 3, "总元素数不正确 %d".formatted(page11_querydsl.getTotalElements()));
Assert.isTrue(page11_querydsl.getContent().size() == 2, "QueryDSL 分页大小不正确 %d".formatted(page11_querydsl.getContent().size()));
Assert.isTrue(page11_querydsl.getTotalElements() == 3, "QueryDSL 总元素数不正确 %d".formatted(page11_querydsl.getTotalElements()));
log.info("QueryDSL 结果: {} 条记录,总记录数: {}", page11_querydsl.getContent().size(), page11_querydsl.getTotalElements());
formatLog("11. 分页查询 HQL");
formatLog("12.4 HQL");
var page11_hql = manager.createQuery(
"""
from Employee employee
@@ -735,10 +819,15 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList().size();
Assert.isTrue(page11_hql.size() == 2, "分页大小不正确 %d".formatted(page11_hql.size()));
Assert.isTrue(total11_hql == 3, "总元素数不正确 %d".formatted(total11_hql));
Assert.isTrue(page11_hql.size() == 2, "HQL 分页大小不正确 %d".formatted(page11_hql.size()));
Assert.isTrue(total11_hql == 3, "HQL 总元素数不正确 %d".formatted(total11_hql));
log.info("HQL 结果: {} 条记录,总记录数: {}", page11_hql.size(), total11_hql);
formatLog("12. CASE WHEN 条件表达式查询 JPA");
// ==================== 测试阶段 13: CASE WHEN 条件表达式查询 ====================
formatLog("阶段 13: CASE WHEN 条件表达式查询");
log.info("查询条件: CASE WHEN age>30 THEN 'Senior' WHEN age BETWEEN 25-30 THEN 'Middle' ELSE 'Junior' END = 'Senior' AND CASE WHEN age>30 THEN 'Senior' WHEN age BETWEEN 25-30 THEN 'Middle' ELSE 'Junior' END != 'Junior'");
formatLog("13.1 JPA Specification");
// 查找年龄大于30Senior或年龄在25-30之间Middle的员工排除Junior级别的员工
var result12_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
cb.equal(
@@ -756,9 +845,10 @@ public class TestApplication extends AbstractTestApplication {
"Junior"
)
));
Assert.isTrue(result12_jpa.size() == 2, "CASE WHEN 查询失败 %d".formatted(result12_jpa.size()));
Assert.isTrue(result12_jpa.size() == 2, "JPA Specification CASE WHEN 查询失败 %d".formatted(result12_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result12_jpa.size());
formatLog("12. CASE WHEN 条件表达式查询 Fenix");
formatLog("13.2 Fenix");
log.info("""
Fenix框架当前版本不支持CASE WHEN条件表达式:
- cb.selectCase() - SQL CASE WHEN表达式
@@ -781,9 +871,10 @@ public class TestApplication extends AbstractTestApplication {
);
}).build();
});
Assert.isTrue(result12_fenix.size() == 2, "CASE WHEN 查询失败 %d".formatted(result12_fenix.size()));
Assert.isTrue(result12_fenix.size() == 2, "Fenix CASE WHEN 查询失败 %d".formatted(result12_fenix.size()));
log.info("Fenix 结果: {} 条记录", result12_fenix.size());
formatLog("12. CASE WHEN 条件表达式查询 QueryDSL");
formatLog("13.3 QueryDSL");
var caseExpr = new CaseBuilder()
.when(QEmployee.employee.age.gt(30)).then("Senior")
.when(QEmployee.employee.age.between(25, 30)).then("Middle")
@@ -791,9 +882,10 @@ public class TestApplication extends AbstractTestApplication {
var result12_querydsl = employeeRepository.findAll(
caseExpr.eq("Senior").and(caseExpr.ne("Junior"))
);
Assert.isTrue(result12_querydsl.size() == 2, "CASE WHEN 查询失败 %d".formatted(result12_querydsl.size()));
Assert.isTrue(result12_querydsl.size() == 2, "QueryDSL CASE WHEN 查询失败 %d".formatted(result12_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result12_querydsl.size());
formatLog("12. CASE WHEN 条件表达式查询 HQL");
formatLog("13.4 HQL");
var result12_hql = manager.createQuery(
"""
from Employee employee
@@ -808,9 +900,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result12_hql.size() == 2, "CASE WHEN 查询失败 %d".formatted(result12_hql.size()));
Assert.isTrue(result12_hql.size() == 2, "HQL CASE WHEN 查询失败 %d".formatted(result12_hql.size()));
log.info("HQL 结果: {} 条记录", result12_hql.size());
formatLog("13. 综合多条件查询 JPA");
// ==================== 测试阶段 14: 综合多条件查询 ====================
formatLog("阶段 14: 综合多条件查询");
log.info("查询条件: company.name='TechCorp' AND company.name!='DataInc' AND skill.name='Java' AND skill.name!='MySQL' AND address.city='Beijing' AND address.city!='Shanghai' AND skills IS NOT EMPTY AND hobbies IS NOT EMPTY AND properties IS NOT EMPTY AND createdTime IS NOT NULL AND modifiedTime IS NOT NULL AND active=true AND name!='Alice Smith' AND age>25 AND role!=USER");
formatLog("14.1 JPA Specification");
// 综合查询公司名为TechCorp、技能包含Java、城市为Beijing、技能和爱好非空、属性非空、创建和修改时间不为null、激活状态为true、姓名不为Alice Smith、年龄大于25、角色不是USER
var result13_jpa = employeeRepository.findAll((root, query, cb) -> {
query.distinct(true);
@@ -839,9 +936,10 @@ public class TestApplication extends AbstractTestApplication {
cb.notEqual(root.get(Employee_.role), Employee.Role.USER)
);
});
Assert.isTrue(result13_jpa.size() == 1 && result13_jpa.get(0).getName().equals("Alice"), "综合多条件查询失败 %d".formatted(result13_jpa.size()));
Assert.isTrue(result13_jpa.size() == 1 && result13_jpa.get(0).getName().equals("Alice"), "JPA Specification 综合多条件查询失败 %d".formatted(result13_jpa.size()));
log.info("JPA Specification 结果: {} 条记录", result13_jpa.size());
formatLog("13. 综合多条件查询 Fenix");
formatLog("14.2 Fenix");
log.info("""
Fenix框架不支持综合多条件查询中的复杂join和集合操作:
- 显式join操作company, skills, properties
@@ -851,7 +949,7 @@ public class TestApplication extends AbstractTestApplication {
Fenix主要支持简单的单表字段查询
可以通过doAny使用原生CriteriaBuilder实现复杂综合查询""");
formatLog("13. 综合多条件查询 QueryDSL");
formatLog("14.3 QueryDSL");
var result13_querydsl = employeeRepository.findAll(
// Company Join 条件
QEmployee.employee.company().name.eq("TechCorp")
@@ -876,9 +974,10 @@ public class TestApplication extends AbstractTestApplication {
.and(QEmployee.employee.age.gt(25))
.and(QEmployee.employee.role.ne(Employee.Role.USER))
);
Assert.isTrue(result13_querydsl.size() == 1 && result13_querydsl.get(0).getName().equals("Alice"), "综合多条件查询失败 %d".formatted(result13_querydsl.size()));
Assert.isTrue(result13_querydsl.size() == 1 && result13_querydsl.get(0).getName().equals("Alice"), "QueryDSL 综合多条件查询失败 %d".formatted(result13_querydsl.size()));
log.info("QueryDSL 结果: {} 条记录", result13_querydsl.size());
formatLog("13. 综合多条件查询 HQL");
formatLog("14.4 HQL");
var result13_hql = manager.createQuery(
"""
select distinct employee
@@ -903,11 +1002,14 @@ public class TestApplication extends AbstractTestApplication {
""",
Employee.class
).getResultList();
Assert.isTrue(result13_hql.size() == 1 && result13_hql.get(0).getName().equals("Alice"), "综合多条件查询失败 %d".formatted(result13_hql.size()));
Assert.isTrue(result13_hql.size() == 1 && result13_hql.get(0).getName().equals("Alice"), "HQL 综合多条件查询失败 %d".formatted(result13_hql.size()));
log.info("HQL 结果: {} 条记录", result13_hql.size());
formatLog("清理测试数据");
// ==================== 测试阶段 15: 清理测试数据 ====================
formatLog("阶段 15: 清理测试数据");
employeeRepository.deleteAllInBatch();
companyRepository.deleteAllInBatch();
log.info("测试数据清理完成");
}
private void testMethodQuery() {