From f81217f14a01bfb4fe4e12c99a1b7f371a4f6793 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Thu, 22 Jan 2026 18:24:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(jpa):=20=E5=A2=9E=E5=8A=A0HQL=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=AE=9E=E7=8E=B0=E5=AF=B9=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../database/jpa/TestApplication.java | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/TestApplication.java b/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/TestApplication.java index a841211..5edea9f 100644 --- a/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/TestApplication.java +++ b/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/TestApplication.java @@ -191,6 +191,21 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result1_querydsl.size() == 1, "基本比较操作符查询失败 (%d)".formatted(result1_querydsl.size())); + formatLog("1. 基本比较操作符查询 HQL"); + var result1_hql = session.createQuery( + """ + from Employee employee + where employee.name = 'Bob' + and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN + and employee.age > 20 + and employee.age < 30 + and employee.salary >= 40000.00 + and employee.salary <= 45000.00 + """, + Employee.class + ).list(); + Assert.isTrue(result1_hql.size() == 1, "基本比较操作符查询失败 (%d)".formatted(result1_hql.size())); + formatLog("2. 区间和集合操作符查询 JPA"); // 查找年龄在25-30之间、年龄不在40-50之间、年龄在25/30/35中、角色是USER或ADMIN、姓名不在Charlie/David中的员工 var result2_jpa = employeeRepository.findAll((root, query, cb) -> cb.and( @@ -225,6 +240,20 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result2_querydsl.size() == 2, "区间和集合操作符查询失败 (%d)".formatted(result2_querydsl.size())); + formatLog("2. 区间和集合操作符查询 HQL"); + var result2_hql = session.createQuery( + """ + from Employee employee + where employee.age between 25 and 30 + and not (employee.age between 40 and 50) + and employee.age in (25, 30, 35) + and employee.role in (com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.USER, com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN) + and employee.name not in ('Charlie', 'David') + """, + Employee.class + ).list(); + Assert.isTrue(result2_hql.size() == 2, "区间和集合操作符查询失败 (%d)".formatted(result2_hql.size())); + formatLog("3. 字符串操作符查询 JPA"); // 查找以A开头、不以C开头、包含"ali"(忽略大小写)、名称长度在4-10之间、前3个字符为"Ali"的员工 var result3_jpa = employeeRepository.findAll((root, query, cb) -> cb.and( @@ -263,6 +292,22 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result3_querydsl.size() == 1, "字符串操作符查询失败 (%d)".formatted(result3_querydsl.size())); + formatLog("3. 字符串操作符查询 HQL"); + var result3_hql = session.createQuery( + """ + from Employee employee + where employee.name like 'A%' + and employee.name not like 'C%' + and lower(employee.name) like '%ali%' + and upper(employee.name) like '%ALI%' + and length(employee.name) > 4 + and length(employee.name) <= 10 + and substring(employee.name, 1, 3) = 'Ali' + """, + Employee.class + ).list(); + Assert.isTrue(result3_hql.size() == 1, "字符串操作符查询失败 (%d)".formatted(result3_hql.size())); + formatLog("4. NULL 和布尔操作符查询 JPA"); // 查找激活状态为true、bonus不为null、code不在E999中的员工 var result4_jpa = employeeRepository.findAll((root, query, cb) -> cb.and( @@ -292,6 +337,18 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(!result4_querydsl.isEmpty(), "NULL 和布尔操作符查询失败 (%d)".formatted(result4_querydsl.size())); + formatLog("4. NULL 和布尔操作符查询 HQL"); + var result4_hql = session.createQuery( + """ + from Employee employee + where employee.active is true + and employee.bonus is not null + and employee.code not in ('E999') + """, + Employee.class + ).list(); + Assert.isTrue(!result4_hql.isEmpty(), "NULL 和布尔操作符查询失败 (%d)".formatted(result4_hql.size())); + formatLog("5. 集合操作符查询 JPA"); // 查找技能集合非空、爱好集合非空、包含"Reading"爱好、不包含"Riding"爱好、爱好数量大于1、技能数量小于4的员工 var result5_jpa = employeeRepository.findAll((root, query, cb) -> cb.and( @@ -322,6 +379,21 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result5_querydsl.size() == 2, "集合操作符查询失败 (%d)".formatted(result5_querydsl.size())); + formatLog("5. 集合操作符查询 HQL"); + var result5_hql = session.createQuery( + """ + from Employee employee + where employee.skills is not empty + and employee.hobbies is not empty + and 'Reading' member of employee.hobbies + and 'Riding' not member of employee.hobbies + and size(employee.hobbies) > 1 + and size(employee.skills) < 4 + """, + Employee.class + ).list(); + Assert.isTrue(result5_hql.size() == 2, "集合操作符查询失败 (%d)".formatted(result5_hql.size())); + formatLog("6. 逻辑操作符查询 JPA"); // 查找姓名为Alice或Bob、且姓名不为Charlie或David的员工 var result6_jpa = employeeRepository.findAll((root, query, cb) -> cb.and( @@ -356,6 +428,17 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result6_querydsl.size() == 2, "逻辑操作符查询失败 (%d)".formatted(result6_querydsl.size())); + formatLog("6. 逻辑操作符查询 HQL"); + var result6_hql = session.createQuery( + """ + from Employee employee + where (employee.name = 'Alice' or employee.name = 'Bob') + and not (employee.name = 'Charlie' or employee.name = 'David') + """, + Employee.class + ).list(); + Assert.isTrue(result6_hql.size() == 2, "逻辑操作符查询失败 (%d)".formatted(result6_hql.size())); + formatLog("7. Specification 链式调用查询 JPA"); // 链式组合:激活状态为true、年龄大于25、角色不是ADMIN、或姓名为Charlie、且姓名不为Alice Smith var result7_jpa = employeeRepository.findAll( @@ -388,6 +471,20 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result7_querydsl.size() == 2, "Specification 链式调用失败 (%d)".formatted(result7_querydsl.size())); + formatLog("7. Specification 链式调用查询 HQL"); + var result7_hql = session.createQuery( + """ + from Employee employee + where employee.active is true + and employee.age > 25 + and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN + and employee.name != 'Alice Smith' + or employee.name = 'Charlie' + """, + Employee.class + ).list(); + Assert.isTrue(result7_hql.size() == 2, "Specification 链式调用失败 (%d)".formatted(result7_hql.size())); + formatLog("8. Join 操作查询 JPA"); // 查找公司名为TechCorp、技能包含Java、属性值为Senior的员工(使用join、fetch、集合join、map join) var result8_jpa = employeeRepository.findAll((root, query, cb) -> { @@ -426,6 +523,24 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result8_querydsl.size() == 1, "Join 操作查询失败 (%d)".formatted(result8_querydsl.size())); + formatLog("8. Join 操作查询 HQL"); + var result8_hql = session.createQuery( + """ + from Employee employee + join employee.company as company + join employee.skills as skill + join employee.properties as prop + where company.name = 'TechCorp' + and company.name != 'DataInc' + and skill.name = 'Java' + and skill.name != 'MySQL' + and value(prop) = 'Senior' + and value(prop) != 'Junior' + """, + Employee.class + ).list(); + Assert.isTrue(result8_hql.size() == 1, "Join 操作查询失败 (%d)".formatted(result8_hql.size())); + formatLog("9. 子查询和聚合函数查询 JPA"); // 查找薪资高于平均薪资、总记录数不为5、总薪酬在55000-70000之间、激活状态为true、姓名不为David、年龄大于28、角色不是USER的员工 var result9_jpa = employeeRepository.findAll((root, query, cb) -> { @@ -478,6 +593,23 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result9_querydsl.isEmpty(), "子查询(聚合函数)+ 数学运算失败 (%d)".formatted(result9_querydsl.size())); + formatLog("9. 子查询和聚合函数查询 HQL"); + var result9_hql = session.createQuery( + """ + from Employee employee + where employee.salary > (select avg(e.salary) from Employee e) + and 5 <> (select count(e.id) from Employee e) + and employee.salary + coalesce(employee.bonus, 0.00) > 55000.00 + and employee.salary + coalesce(employee.bonus, 0.00) < 70000.00 + and employee.active is true + and employee.name != 'David' + and employee.age > 28 + and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.USER + """, + Employee.class + ).list(); + Assert.isTrue(result9_hql.isEmpty(), "子查询(聚合函数)+ 数学运算失败 (%d)".formatted(result9_hql.size())); + formatLog("10. 排序查询 JPA"); // 查找激活状态为true、角色不是ADMIN、年龄大于20、薪资小于60000的员工,按年龄降序、姓名升序排序 var result10_jpa = employeeRepository.findAll( @@ -521,6 +653,20 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result10_querydsl.size() == 3, "排序查询失败 (%d)".formatted(result10_querydsl.size())); + formatLog("10. 排序查询 HQL"); + var result10_hql = session.createQuery( + """ + from Employee employee + where employee.active is true + and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN + and employee.age > 20 + and employee.salary < 60000.00 + order by employee.age desc, employee.name asc + """, + Employee.class + ).list(); + Assert.isTrue(result10_hql.size() == 3, "排序查询失败 (%d)".formatted(result10_hql.size())); + formatLog("11. 分页查询 JPA"); // 分页查找激活状态为true、角色不是ADMIN、年龄大于20、薪资小于60000的员工,每页2条,按年龄排序 var page11_jpa = employeeRepository.findAll( @@ -564,6 +710,31 @@ public class TestApplication extends AbstractTestApplication { Assert.isTrue(page11_querydsl.getContent().size() == 2, "分页大小不正确 (%d)".formatted(page11_querydsl.getContent().size())); Assert.isTrue(page11_querydsl.getTotalElements() == 3, "总元素数不正确 (%d)".formatted(page11_querydsl.getTotalElements())); + formatLog("11. 分页查询 HQL"); + var page11_hql = session.createQuery( + """ + from Employee employee + where employee.active is true + and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN + and employee.age > 20 + and employee.salary < 60000.00 + order by employee.age + """, + Employee.class + ).setFirstResult(0).setMaxResults(2).list(); + var total11_hql = session.createQuery( + """ + from Employee employee + where employee.active is true + and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.ADMIN + and employee.age > 20 + and employee.salary < 60000.00 + """, + Employee.class + ).list().size(); + Assert.isTrue(page11_hql.size() == 2, "分页大小不正确 (%d)".formatted(page11_hql.size())); + Assert.isTrue(total11_hql == 3, "总元素数不正确 (%d)".formatted(total11_hql)); + formatLog("12. CASE WHEN 条件表达式查询 JPA"); // 查找年龄大于30(Senior)或年龄在25-30之间(Middle)的员工,排除Junior级别的员工 var result12_jpa = employeeRepository.findAll((root, query, cb) -> cb.and( @@ -618,6 +789,23 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result12_querydsl.size() == 2, "CASE WHEN 查询失败 (%d)".formatted(result12_querydsl.size())); + formatLog("12. CASE WHEN 条件表达式查询 HQL"); + var result12_hql = session.createQuery( + """ + from Employee employee + where (case when employee.age > 30 then 'Senior' + when employee.age between 25 and 30 then 'Middle' + else 'Junior' + end) = 'Senior' + and (case when employee.age > 30 then 'Senior' + when employee.age between 25 and 30 then 'Middle' + else 'Junior' + end) != 'Junior' + """, + Employee.class + ).list(); + Assert.isTrue(result12_hql.size() == 2, "CASE WHEN 查询失败 (%d)".formatted(result12_hql.size())); + formatLog("13. 综合多条件查询 JPA"); // 综合查询:公司名为TechCorp、技能包含Java、城市为Beijing、技能和爱好非空、属性非空、创建和修改时间不为null、激活状态为true、姓名不为Alice Smith、年龄大于25、角色不是USER var result13_jpa = employeeRepository.findAll((root, query, cb) -> { @@ -685,6 +873,33 @@ public class TestApplication extends AbstractTestApplication { ); Assert.isTrue(result13_querydsl.size() == 1 && result13_querydsl.get(0).getName().equals("Alice"), "综合多条件查询失败 (%d)".formatted(result13_querydsl.size())); + formatLog("13. 综合多条件查询 HQL"); + var result13_hql = session.createQuery( + """ + select distinct employee + from Employee employee + join employee.company as company + join employee.skills as skill + where company.name = 'TechCorp' + and company.name != 'DataInc' + and skill.name = 'Java' + and skill.name != 'MySQL' + and employee.address.city = 'Beijing' + and employee.address.city != 'Shanghai' + and employee.skills is not empty + and employee.hobbies is not empty + and employee.properties is not empty + and employee.createdTime is not null + and employee.modifiedTime is not null + and employee.active is true + and employee.name != 'Alice Smith' + and employee.age > 25 + and employee.role != com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee.Role.USER + """, + Employee.class + ).list(); + Assert.isTrue(result13_hql.size() == 1 && result13_hql.get(0).getName().equals("Alice"), "综合多条件查询失败 (%d)".formatted(result13_hql.size())); + formatLog("清理测试数据"); employeeRepository.deleteAllInBatch(); companyRepository.deleteAllInBatch();