diff --git a/spring-boot-service-template-database/spring-boot-service-template-database-jpa/pom.xml b/spring-boot-service-template-database/spring-boot-service-template-database-jpa/pom.xml
index 839342d..e0d393b 100644
--- a/spring-boot-service-template-database/spring-boot-service-template-database-jpa/pom.xml
+++ b/spring-boot-service-template-database/spring-boot-service-template-database-jpa/pom.xml
@@ -94,6 +94,10 @@
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
\ No newline at end of file
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 429faf6..6f90c44 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
@@ -56,7 +56,7 @@ public class TestApplication extends AbstractTestApplication {
testCrud();
testDelete();
testQuery();
- testNative();
+ testMethodQuery();
System.exit(0);
}
@@ -910,7 +910,383 @@ public class TestApplication extends AbstractTestApplication {
companyRepository.deleteAllInBatch();
}
- private void testNative() {
+ private void testMethodQuery() {
+ formatLog("准备 Query Method 测试数据");
+ 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());
+ var skill1 = Skill.builder().name("Java").description("Java 编程语言").build();
+ var skill2 = Skill.builder().name("Python").description("Python 编程语言").build();
+ var skill3 = Skill.builder().name("Spring").description("Spring 框架").build();
+ var skill4 = Skill.builder().name("MySQL").description("MySQL 数据库").build();
+
+ var emp1 = employeeRepository.save(Employee.builder()
+ .name("Alice").age(30).role(Employee.Role.ADMIN).code("E001")
+ .salary(new BigDecimal("50000.00")).bonus(new BigDecimal("5000.00"))
+ .active(true).company(company1)
+ .address(Address.builder().street("Main St").city("Beijing").state("Beijing").zipCode("100000").country("China").build())
+ .skills(Set.of(skill1, skill3))
+ .hobbies(List.of("Reading", "Swimming"))
+ .properties(Map.of("department", "Engineering", "level", "Senior"))
+ .connections(Map.of(Employee.ConnectionType.EMAIL, "alice@example.com"))
+ .build());
+
+ var emp2 = employeeRepository.save(Employee.builder()
+ .name("Bob").age(25).role(Employee.Role.USER).code("E002")
+ .salary(new BigDecimal("40000.00")).bonus(new BigDecimal("4000.00"))
+ .active(true).company(company2)
+ .address(Address.builder().street("Oak Ave").city("Shanghai").state("Shanghai").zipCode("200000").country("China").build())
+ .skills(Set.of(skill2))
+ .hobbies(List.of("Gaming"))
+ .properties(Map.of("department", "Marketing", "level", "Junior"))
+ .connections(Map.of(Employee.ConnectionType.PHONE, "1234567890"))
+ .build());
+
+ var emp3 = employeeRepository.save(Employee.builder()
+ .name("Charlie").age(35).role(Employee.Role.ADMIN).code("E003")
+ .salary(new BigDecimal("60000.00")).bonus(new BigDecimal("6000.00"))
+ .active(false).company(company1)
+ .address(Address.builder().street("Pine Rd").city("Shenzhen").state("Guangdong").zipCode("518000").country("China").build())
+ .skills(Set.of(skill1, skill2, skill3))
+ .hobbies(List.of("Reading", "Gaming", "Music"))
+ .properties(Map.of("department", "Engineering", "level", "Lead"))
+ .connections(Map.of(Employee.ConnectionType.EMAIL, "charlie@example.com", Employee.ConnectionType.PHONE, "0987654321"))
+ .build());
+
+ var emp4 = employeeRepository.save(Employee.builder()
+ .name("David").age(28).role(Employee.Role.USER).code("E004")
+ .salary(new BigDecimal("45000.00")).bonus(null)
+ .active(true).company(company3)
+ .address(Address.builder().street("Elm Ln").city("Guangzhou").state("Guangdong").zipCode("510000").country("China").build())
+ .skills(Set.of(skill4))
+ .hobbies(List.of())
+ .properties(Map.of())
+ .connections(Map.of())
+ .build());
+
+ var emp5 = employeeRepository.save(Employee.builder()
+ .name("Alice Smith").age(32).role(Employee.Role.USER).code("E005")
+ .salary(new BigDecimal("55000.00")).bonus(new BigDecimal("5500.00"))
+ .active(true).company(company2)
+ .address(Address.builder().street("Maple Dr").city("Hangzhou").state("Zhejiang").zipCode("310000").country("China").build())
+ .skills(Set.of(skill1, skill4))
+ .hobbies(List.of("Swimming", "Music"))
+ .properties(Map.of("department", "Sales", "level", "Middle"))
+ .connections(Map.of(Employee.ConnectionType.EMAIL, "alicesmith@example.com"))
+ .build());
+
+ // ==================== 1. 基本字段查询测试 ====================
+
+ formatLog("1.1 单字段精确匹配查询");
+ var r1_1 = employeeRepository.findByName("Bob");
+ Assert.isTrue(!r1_1.isEmpty(), "findByName failed");
+ Assert.isTrue(r1_1.get(0).getName().equals("Bob"), "findByName 结果不正确");
+
+ var r1_2 = employeeRepository.findByCode("E003");
+ Assert.isTrue(r1_2.isPresent(), "findByCode failed");
+ Assert.isTrue(r1_2.orElseThrow().getCode().equals("E003"), "findByCode 结果不正确");
+
+ var r1_3 = employeeRepository.findByRole(Employee.Role.ADMIN);
+ Assert.isTrue(r1_3.size() == 2, "findByRole 结果数量应为2,实际:" + r1_3.size());
+
+ formatLog("1.2 布尔值查询");
+ var r1_4 = employeeRepository.findByActiveTrue();
+ Assert.isTrue(r1_4.size() == 4, "findByActiveTrue 结果数量应为4,实际:" + r1_4.size());
+
+ var r1_5 = employeeRepository.findByActiveFalse();
+ Assert.isTrue(r1_5.size() == 1, "findByActiveFalse 结果数量应为1,实际:" + r1_5.size());
+
+ var r1_6 = employeeRepository.countByActiveTrue();
+ Assert.isTrue(r1_6 == 4, "countByActiveTrue 结果应为4,实际:" + r1_6);
+
+ // ==================== 2. 比较运算符查询测试 ====================
+
+ formatLog("2.1 大于/小于/等于/不等于查询");
+ var r2_1 = employeeRepository.findByAgeGreaterThan(30);
+ Assert.isTrue(r2_1.size() == 2, "findByAgeGreaterThan(30) 结果数量应为2,实际:" + r2_1.size());
+
+ var r2_2 = employeeRepository.findByAgeLessThan(30);
+ Assert.isTrue(r2_2.size() == 2, "findByAgeLessThan(30) 结果数量应为2,实际:" + r2_2.size());
+
+ var r2_3 = employeeRepository.findByAgeGreaterThanEqual(30);
+ Assert.isTrue(r2_3.size() == 3, "findByAgeGreaterThanEqual(30) 结果数量应为3,实际:" + r2_3.size());
+
+ var r2_4 = employeeRepository.findByAgeLessThanEqual(30);
+ Assert.isTrue(r2_4.size() == 3, "findByAgeLessThanEqual(30) 结果数量应为3,实际:" + r2_4.size());
+
+ var r2_5 = employeeRepository.findByAgeBetween(25, 35);
+ Assert.isTrue(r2_5.size() == 4, "findByAgeBetween(25,35) 结果数量应为4,实际:" + r2_5.size());
+
+ formatLog("2.2 薪资范围查询");
+ var r2_6 = employeeRepository.findBySalaryGreaterThan(new BigDecimal("50000.00"));
+ Assert.isTrue(r2_6.size() == 3, "findBySalaryGreaterThan 结果数量应为3,实际:" + r2_6.size());
+
+ var r2_7 = employeeRepository.findBySalaryLessThan(new BigDecimal("50000.00"));
+ Assert.isTrue(r2_7.size() == 2, "findBySalaryLessThan 结果数量应为2,实际:" + r2_7.size());
+
+ var r2_8 = employeeRepository.findBySalaryBetween(new BigDecimal("40000.00"), new BigDecimal("55000.00"));
+ Assert.isTrue(r2_8.size() == 4, "findBySalaryBetween 结果数量应为4,实际:" + r2_8.size());
+
+ // ==================== 3. 字符串匹配查询测试 ====================
+
+ formatLog("3.1 字符串包含/开头/结尾查询");
+ var r3_1 = employeeRepository.findByNameContaining("Alice");
+ Assert.isTrue(r3_1.size() == 2, "findByNameContaining('Alice') 结果数量应为2,实际:" + r3_1.size());
+
+ var r3_2 = employeeRepository.findByNameStartingWith("A");
+ Assert.isTrue(r3_2.size() == 2, "findByNameStartingWith('A') 结果数量应为2,实际:" + r3_2.size());
+
+ var r3_3 = employeeRepository.findByNameEndingWith("e");
+ Assert.isTrue(r3_3.size() == 3, "findByNameEndingWith('e') 结果数量应为3,实际:" + r3_3.size());
+
+ var r3_4 = employeeRepository.findByNameLike("%ar%");
+ Assert.isTrue(r3_4.size() == 1, "findByNameLike('%ar%') 结果数量应为1,实际:" + r3_4.size());
+
+ formatLog("3.2 忽略大小写查询");
+ var r3_5 = employeeRepository.findByNameContainingIgnoreCase("ALICE");
+ Assert.isTrue(r3_5.size() == 2, "findByNameContainingIgnoreCase('ALICE') 结果数量应为2,实际:" + r3_5.size());
+
+ var r3_6 = employeeRepository.findByNameStartingWithIgnoreCase("B");
+ Assert.isTrue(r3_6.size() == 1, "findByNameStartingWithIgnoreCase('B') 结果数量应为1,实际:" + r3_6.size());
+
+ var r3_7 = employeeRepository.findByNameIgnoreCase("BOB");
+ Assert.notNull(r3_7, "findByNameIgnoreCase('BOB') 返回null");
+
+ // ==================== 4. NULL值和空值查询测试 ====================
+
+ formatLog("4.1 NULL值查询");
+ var r4_1 = employeeRepository.findByBonusIsNull();
+ Assert.isTrue(r4_1.size() == 1, "findByBonusIsNull 结果数量应为1,实际:" + r4_1.size());
+ Assert.isTrue(r4_1.get(0).getName().equals("David"), "findByBonusIsNull 结果应为David");
+
+ var r4_2 = employeeRepository.findByBonusIsNotNull();
+ Assert.isTrue(r4_2.size() == 4, "findByBonusIsNotNull 结果数量应为4,实际:" + r4_2.size());
+
+ var r4_3 = employeeRepository.findByResumeIsNull();
+ Assert.isTrue(r4_3.size() == 5, "findByResumeIsNull 结果数量应为5,实际:" + r4_3.size());
+
+ formatLog("4.2 空集合查询");
+ var r4_4 = employeeRepository.findByHobbiesEmpty();
+ Assert.isTrue(r4_4.size() == 1, "findByHobbiesEmpty 结果数量应为1,实际:" + r4_4.size());
+
+ var r4_5 = employeeRepository.findByHobbiesIsNotEmpty();
+ Assert.isTrue(r4_5.size() == 4, "findByHobbiesIsNotEmpty 结果数量应为4,实际:" + r4_5.size());
+
+ // ==================== 5. 集合成员查询测试 ====================
+
+ formatLog("5.1 IN/NOT IN 查询");
+ var r5_1 = employeeRepository.findByRoleIn(Set.of(Employee.Role.ADMIN));
+ Assert.isTrue(r5_1.size() == 2, "findByRoleIn 结果数量应为2,实际:" + r5_1.size());
+
+ var r5_2 = employeeRepository.findByRoleNotIn(Set.of(Employee.Role.ADMIN));
+ Assert.isTrue(r5_2.size() == 3, "findByRoleNotIn 结果数量应为3,实际:" + r5_2.size());
+
+ var r5_3 = employeeRepository.findByNameIn(List.of("Alice", "Bob"));
+ Assert.isTrue(r5_3.size() == 2, "findByNameIn 结果数量应为2,实际:" + r5_3.size());
+
+ var r5_4 = employeeRepository.findByNameNotIn(List.of("Charlie", "David"));
+ Assert.isTrue(r5_4.size() == 3, "findByNameNotIn 结果数量应为3,实际:" + r5_4.size());
+
+ var r5_5 = employeeRepository.findByAgeIn(List.of(25, 30, 35));
+ Assert.isTrue(r5_5.size() == 3, "findByAgeIn 结果数量应为3,实际:" + r5_5.size());
+
+ // ==================== 6. 逻辑运算查询测试 ====================
+
+ formatLog("6.1 AND 组合查询");
+ var r6_1 = employeeRepository.findByNameAndRole("Alice", Employee.Role.ADMIN);
+ Assert.isTrue(r6_1.size() == 1, "findByNameAndRole 结果数量应为1,实际:" + r6_1.size());
+
+ var r6_2 = employeeRepository.findByAgeAndActive(30, true);
+ Assert.isTrue(r6_2.size() == 1, "findByAgeAndActive 结果数量应为1,实际:" + r6_2.size());
+
+ var r6_3 = employeeRepository.findByNameAndSalaryGreaterThan("Alice", new BigDecimal("40000.00"));
+ Assert.isTrue(r6_3.size() == 1, "findByNameAndSalaryGreaterThan 结果数量应为1,实际:" + r6_3.size());
+
+ formatLog("6.2 OR 组合查询");
+ var r6_4 = employeeRepository.findByNameOrRole("Alice", Employee.Role.USER);
+ Assert.isTrue(r6_4.size() == 4, "findByNameOrRole 结果数量应为4,实际:" + r6_4.size());
+
+ var r6_5 = employeeRepository.findByAgeLessThanOrSalaryGreaterThan(30, new BigDecimal("55000.00"));
+ Assert.isTrue(r6_5.size() == 3, "findByAgeLessThanOrSalaryGreaterThan 结果数量应为3,实际:" + r6_5.size());
+
+ var r6_6 = employeeRepository.findByNameOrCode("Alice", "E003");
+ Assert.isTrue(r6_6.size() == 2, "findByNameOrCode 结果数量应为2,实际:" + r6_6.size());
+
+ formatLog("6.3 NOT 查询");
+ var r6_7 = employeeRepository.findByRoleNot(Employee.Role.ADMIN);
+ Assert.isTrue(r6_7.size() == 3, "findByRoleNot 结果数量应为3,实际:" + r6_7.size());
+
+ var r6_8 = employeeRepository.findByNameNot("Alice");
+ Assert.isTrue(r6_8.size() == 4, "findByNameNot 结果数量应为4,实际:" + r6_8.size());
+
+ // ==================== 7. 排序查询测试 ====================
+
+ formatLog("7.1 单字段排序查询");
+ var r7_1 = employeeRepository.findByActiveTrueOrderByAgeAsc();
+ Assert.isTrue(r7_1.size() == 4, "findByActiveTrueOrderByAgeAsc 结果数量应为4");
+ Assert.isTrue(r7_1.get(0).getAge() == 25, "findByActiveTrueOrderByAgeAsc 第一条年龄应为25");
+
+ var r7_2 = employeeRepository.findByActiveTrueOrderByAgeDesc();
+ Assert.isTrue(r7_2.size() == 4, "findByActiveTrueOrderByAgeDesc 结果数量应为4");
+ Assert.isTrue(r7_2.get(0).getAge() == 32, "findByActiveTrueOrderByAgeDesc 第一条年龄应为32");
+
+ var r7_3 = employeeRepository.findByActiveTrueOrderBySalaryDesc();
+ Assert.isTrue(r7_3.size() == 4, "findByActiveTrueOrderBySalaryDesc 结果数量应为4");
+ Assert.isTrue(r7_3.get(0).getSalary().compareTo(new BigDecimal("55000.00")) == 0, "findByActiveTrueOrderBySalaryDesc 第一条薪资应为55000");
+
+ formatLog("7.2 多字段排序查询");
+ var r7_4 = employeeRepository.findByRoleOrderByAgeDescNameAsc(Employee.Role.ADMIN);
+ Assert.isTrue(r7_4.size() == 2, "findByRoleOrderByAgeDescNameAsc 结果数量应为2");
+
+ // ==================== 8. 分页查询测试 ====================
+
+ formatLog("8.1 分页查询");
+ var r8_1 = employeeRepository.findByActiveTrue(PageRequest.of(0, 2));
+ Assert.isTrue(r8_1.getContent().size() == 2, "分页结果数量应为2,实际:" + r8_1.getContent().size());
+ Assert.isTrue(r8_1.getTotalElements() == 4, "总元素数应为4,实际:" + r8_1.getTotalElements());
+
+ var r8_2 = employeeRepository.findByRole(Employee.Role.USER, PageRequest.of(0, 10));
+ Assert.isTrue(r8_2.getContent().size() == 3, "findByRole 分页结果数量应为3,实际:" + r8_2.getContent().size());
+
+ var r8_3 = employeeRepository.findBySalaryGreaterThan(new BigDecimal("45000.00"), PageRequest.of(0, 2));
+ Assert.isTrue(r8_3.getContent().size() == 2, "findBySalaryGreaterThan 分页结果数量应为2");
+
+ // ==================== 9. 关联查询测试 ====================
+
+ formatLog("9.1 公司关联查询");
+ var r9_1 = employeeRepository.findByCompany(company1);
+ Assert.isTrue(r9_1.size() == 2, "findByCompany 结果数量应为2,实际:" + r9_1.size());
+
+ var r9_2 = employeeRepository.findByCompanyName("TechCorp");
+ Assert.isTrue(r9_2.size() == 2, "findByCompanyName 结果数量应为2,实际:" + r9_2.size());
+
+ var r9_3 = employeeRepository.findByCompanyNameContaining("Corp");
+ Assert.isTrue(r9_3.size() == 2, "findByCompanyNameContaining 结果数量应为2,实际:" + r9_3.size());
+
+ var r9_4 = employeeRepository.findByCompanyMembersGreaterThan(75);
+ Assert.isTrue(r9_4.size() == 2, "findByCompanyMembersGreaterThan 结果数量应为2,实际:" + r9_4.size());
+
+ formatLog("9.2 技能关联查询");
+ var r9_5 = employeeRepository.findBySkillsContaining(skill1);
+ Assert.isTrue(r9_5.size() == 3, "findBySkillsContaining 结果数量应为3,实际:" + r9_5.size());
+
+ var r9_6 = employeeRepository.findBySkillsName("Java");
+ Assert.isTrue(r9_6.size() == 3, "findBySkillsName 结果数量应为3,实际:" + r9_6.size());
+
+ var r9_7 = employeeRepository.findBySkillsNameContaining("Pyt");
+ Assert.isTrue(r9_7.size() == 2, "findBySkillsNameContaining 结果数量应为2,实际:" + r9_7.size());
+
+ var r9_8 = employeeRepository.findBySkillsNameIn(List.of("Java", "Python"));
+ Assert.isTrue(r9_8.size() == 3, "findBySkillsNameIn 结果数量应为3,实际:" + r9_8.size());
+
+ // ==================== 10. 嵌入式对象查询测试 ====================
+
+ formatLog("10.1 嵌入式对象查询");
+ var r10_1 = employeeRepository.findByAddressCity("Beijing");
+ Assert.isTrue(r10_1.size() == 1, "findByAddressCity 结果数量应为1,实际:" + r10_1.size());
+ Assert.isTrue(r10_1.get(0).getName().equals("Alice"), "findByAddressCity 结果应为Alice");
+
+ var r10_2 = employeeRepository.findByAddressCityContaining("ang");
+ Assert.isTrue(r10_2.size() == 2, "findByAddressCityContaining 结果数量应为2,实际:" + r10_2.size());
+
+ var r10_3 = employeeRepository.findByAddressState("Guangdong");
+ Assert.isTrue(r10_3.size() == 2, "findByAddressState 结果数量应为2,实际:" + r10_3.size());
+
+ var r10_4 = employeeRepository.findByAddressCountry("China");
+ Assert.isTrue(r10_4.size() == 5, "findByAddressCountry 结果数量应为5,实际:" + r10_4.size());
+
+ var r10_5 = employeeRepository.findByAddressCityAndAddressState("Shenzhen", "Guangdong");
+ Assert.isTrue(r10_5.size() == 1, "findByAddressCityAndAddressState 结果数量应为1");
+
+ // ==================== 11. 复合复杂查询测试 ====================
+
+ formatLog("11.1 多条件组合查询");
+ var r11_1 = employeeRepository.findByNameAndRoleAndAgeGreaterThan("Alice", Employee.Role.ADMIN, 25);
+ Assert.isTrue(r11_1.size() == 1, "findByNameAndRoleAndAgeGreaterThan 结果数量应为1");
+
+ var r11_2 = employeeRepository.findByRoleAndActiveTrueAndSalaryGreaterThan(Employee.Role.USER, new BigDecimal("40000.00"));
+ Assert.isTrue(r11_2.size() == 2, "findByRoleAndActiveTrueAndSalaryGreaterThan 结果数量应为2");
+
+ var r11_3 = employeeRepository.findByNameContainingIgnoreCaseAndActiveTrueAndAgeBetween("a", 25, 35);
+ Assert.isTrue(r11_3.size() == 3, "findByNameContainingIgnoreCaseAndActiveTrueAndAgeBetween 结果数量应为3");
+
+ formatLog("11.2 EXISTS语义查询");
+ var r11_4 = employeeRepository.findByCompanyNameContainingAndActiveTrue("Corp");
+ Assert.isTrue(r11_4.size() == 2, "findByCompanyNameContainingAndActiveTrue 结果数量应为2");
+
+ var r11_5 = employeeRepository.findBySkillsNameContainingAndAgeGreaterThan("Java", 25);
+ Assert.isTrue(r11_5.size() == 2, "findBySkillsNameContainingAndAgeGreaterThan 结果数量应为2");
+
+ // ==================== 12. DISTINCT 查询测试 ====================
+
+ formatLog("12.1 DISTINCT 查询");
+ var r12_1 = employeeRepository.findDistinctByRole(Employee.Role.ADMIN);
+ Assert.isTrue(r12_1.size() == 2, "findDistinctByRole 结果数量应为2");
+
+ // ==================== 13. TOP/LIMIT 查询测试 ====================
+
+ formatLog("13.1 TOP/LIMIT 查询");
+ var r13_1 = employeeRepository.findTop5BySalaryGreaterThan(new BigDecimal("40000.00"));
+ Assert.isTrue(r13_1.size() == 4, "findTop5BySalaryGreaterThan 结果数量应为4");
+
+ var r13_3 = employeeRepository.findFirstByRoleOrderBySalaryDesc(Employee.Role.ADMIN);
+ Assert.notNull(r13_3, "findFirstByRoleOrderBySalaryDesc 返回null");
+ Assert.isTrue(r13_3.orElseThrow().getSalary().compareTo(new BigDecimal("60000.00")) == 0, "findFirstByRoleOrderBySalaryDesc 最高薪资应为60000");
+
+ // ==================== 14. LIKE 模式查询测试 ====================
+
+ formatLog("14.1 LIKE 模式查询");
+ var r14_1 = employeeRepository.findByNameLikeIgnoreCase("%ALICE%");
+ Assert.isTrue(r14_1.size() == 2, "findByNameLikeIgnoreCase 结果数量应为2");
+
+ var r14_2 = employeeRepository.findByNameStartingWithAndRole("A", Employee.Role.ADMIN);
+ Assert.isTrue(r14_2.size() == 1, "findByNameStartingWithAndRole 结果数量应为1");
+
+ var r14_3 = employeeRepository.findByNameEndingWithAndAgeLessThan("e", 30);
+ Assert.isTrue(r14_3.size() == 1, "findByNameEndingWithAndAgeLessThan 结果数量应为1");
+
+ // ==================== 15. 其他未覆盖的有用方法测试 ====================
+
+ formatLog("15.1 聚合计数查询");
+ var r15_1 = employeeRepository.countByActiveTrue();
+ Assert.isTrue(r15_1 == 4, "countByActiveTrue 结果应为4,实际:" + r15_1);
+
+ var r15_2 = employeeRepository.countByActiveFalse();
+ Assert.isTrue(r15_2 == 1, "countByActiveFalse 结果应为1,实际:" + r15_2);
+
+ formatLog("15.2 技能和简历查询");
+ var r15_3 = employeeRepository.findByResumeIsNotNull();
+ Assert.isTrue(r15_3.size() == 5, "findByResumeIsNotNull 结果数量应为5,实际:" + r15_3.size());
+
+ var r15_4 = employeeRepository.findBySkillsEmpty();
+ Assert.isTrue(r15_4.isEmpty(), "findBySkillsEmpty 结果应该为空,实际:" + r15_4.size());
+
+ formatLog("15.3 公司成员数查询");
+ var r15_5 = employeeRepository.findByCompanyMembersGreaterThan(100);
+ Assert.isTrue(r15_5.size() == 1, "findByCompanyMembersGreaterThan(100) 结果数量应为1,实际:" + r15_5.size());
+
+ var r15_6 = employeeRepository.findByCompanyMembersGreaterThan(50);
+ Assert.isTrue(r15_6.size() == 3, "findByCompanyMembersGreaterThan(50) 结果数量应为3,实际:" + r15_6.size());
+
+ formatLog("15.4 城市包含查询");
+ var r15_7 = employeeRepository.findByAddressCityContaining("ang");
+ Assert.isTrue(r15_7.size() == 2, "findByAddressCityContaining('ang') 结果数量应为2,实际:" + r15_7.size());
+
+ formatLog("15.5 DISTINCT 查询");
+ var r15_8 = employeeRepository.findDistinctByRole(Employee.Role.USER);
+ Assert.isTrue(r15_8.size() == 3, "findDistinctByRole 结果数量应为3,实际:" + r15_8.size());
+
+ formatLog("15.6 TOP/LIMIT 查询");
+ var r15_9 = employeeRepository.findTop5BySalaryGreaterThan(new BigDecimal("40000.00"));
+ Assert.isTrue(r15_9.size() == 4, "findTop5BySalaryGreaterThan 结果数量应为4,实际:" + r15_9.size());
+
+ var r15_10 = employeeRepository.findFirstByRoleOrderBySalaryDesc(Employee.Role.ADMIN);
+ Assert.isTrue(r15_10.isPresent(), "findFirstByRoleOrderBySalaryDesc 返回null");
+ Assert.isTrue(r15_10.orElseThrow().getSalary().compareTo(new BigDecimal("60000.00")) == 0, "findFirstByRoleOrderBySalaryDesc 最高薪资应为60000");
+
+ formatLog("清理测试数据");
+ employeeRepository.deleteAllInBatch();
+ companyRepository.deleteAllInBatch();
}
}
diff --git a/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/repository/EmployeeRepository.java b/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/repository/EmployeeRepository.java
index 826258c..a522168 100644
--- a/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/repository/EmployeeRepository.java
+++ b/spring-boot-service-template-database/spring-boot-service-template-database-jpa/src/test/java/com/lanyuanxiaoyao/service/template/database/jpa/repository/EmployeeRepository.java
@@ -1,7 +1,14 @@
package com.lanyuanxiaoyao.service.template.database.jpa.repository;
+import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company;
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee;
+import com.lanyuanxiaoyao.service.template.database.jpa.entity.Skill;
+import java.math.BigDecimal;
+import java.util.List;
import java.util.Optional;
+import java.util.Set;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.stereotype.Repository;
@@ -12,4 +19,194 @@ public interface EmployeeRepository extends SimpleRepository {
@EntityGraph(attributePaths = {"company"})
@Override
Optional findOne(Specification specification);
+
+ // ==================== 1. 基本字段查询 ====================
+
+ // 单字段精确匹配
+ List findByName(String name);
+
+ Optional findByCode(String code);
+
+ List findByRole(Employee.Role role);
+
+ // 布尔值查询
+ List findByActiveTrue();
+
+ List findByActiveFalse();
+
+ long countByActiveTrue();
+
+ long countByActiveFalse();
+
+ // ==================== 2. 比较运算符查询 ====================
+
+ List findByAgeGreaterThan(Integer age);
+
+ List findByAgeLessThan(Integer age);
+
+ List findByAgeGreaterThanEqual(Integer age);
+
+ List findByAgeLessThanEqual(Integer age);
+
+ List findByAgeBetween(Integer startAge, Integer endAge);
+
+ List findBySalaryGreaterThan(BigDecimal salary);
+
+ List findBySalaryLessThan(BigDecimal salary);
+
+ List findBySalaryGreaterThanEqual(BigDecimal salary);
+
+ List findBySalaryLessThanEqual(BigDecimal salary);
+
+ List findBySalaryBetween(BigDecimal minSalary, BigDecimal maxSalary);
+
+ // ==================== 3. 字符串匹配查询 ====================
+
+ // 精确匹配
+ List findByNameContaining(String name);
+
+ List findByNameStartingWith(String prefix);
+
+ List findByNameEndingWith(String suffix);
+
+ List findByNameLike(String pattern);
+
+ // 忽略大小写
+ List findByNameContainingIgnoreCase(String name);
+
+ List findByNameStartingWithIgnoreCase(String prefix);
+
+ List findByNameEndingWithIgnoreCase(String suffix);
+
+ List findByNameIgnoreCase(String name);
+
+ // ==================== 4. NULL值和空值查询 ====================
+
+ List findByBonusIsNull();
+
+ List findByBonusIsNotNull();
+
+ List findByResumeIsNull();
+
+ List findByResumeIsNotNull();
+
+ List findByHobbiesEmpty();
+
+ List findByHobbiesIsNotEmpty();
+
+ List findBySkillsEmpty();
+
+ List findBySkillsIsNotEmpty();
+
+ // ==================== 5. 集合成员查询 (IN/NOT IN) ====================
+
+ List findByRoleIn(Set roles);
+
+ List findByRoleNotIn(Set roles);
+
+ List findByNameIn(List names);
+
+ List findByNameNotIn(List names);
+
+ List findByAgeIn(List ages);
+
+ List findByAgeNotIn(List ages);
+
+ // ==================== 6. 逻辑运算查询 (AND/OR/NOT) ====================
+
+ List findByNameAndRole(String name, Employee.Role role);
+
+ List findByAgeAndActive(Integer age, Boolean active);
+
+ List findByNameAndSalaryGreaterThan(String name, BigDecimal salary);
+
+ List findByRoleAndActiveAndAgeGreaterThan(Employee.Role role, Boolean active, Integer age);
+
+ List findByNameOrRole(String name, Employee.Role role);
+
+ List findByAgeLessThanOrSalaryGreaterThan(Integer age, BigDecimal salary);
+
+ List findByNameOrCode(String name, String code);
+
+ List findByRoleNot(Employee.Role role);
+
+ List findByNameNot(String name);
+
+ // ==================== 7. 排序查询 ====================
+
+ List findByActiveTrueOrderByAgeAsc();
+
+ List findByActiveTrueOrderByAgeDesc();
+
+ List findByActiveTrueOrderBySalaryDesc();
+
+ List findByRoleOrderByAgeDescNameAsc(Employee.Role role);
+
+ // ==================== 8. 分页查询 ====================
+
+ Page findByActiveTrue(Pageable pageable);
+
+ Page findByRole(Employee.Role role, Pageable pageable);
+
+ Page findBySalaryGreaterThan(BigDecimal salary, Pageable pageable);
+
+ // ==================== 9. 关联查询 (JOIN) ====================
+
+ List findByCompany(Company company);
+
+ List findByCompanyName(String companyName);
+
+ List findByCompanyNameContaining(String companyName);
+
+ List findByCompanyMembersGreaterThan(Integer members);
+
+ List findBySkillsContaining(Skill skill);
+
+ List findBySkillsName(String skillName);
+
+ List findBySkillsNameContaining(String skillName);
+
+ List findBySkillsNameIn(List skillNames);
+
+ // ==================== 10. 嵌入式对象查询 ====================
+
+ List findByAddressCity(String city);
+
+ List findByAddressCityContaining(String city);
+
+ List findByAddressState(String state);
+
+ List findByAddressCountry(String country);
+
+ List findByAddressCityAndAddressState(String city, String state);
+
+ // ==================== 11. 复合复杂查询 ====================
+
+ List findByNameAndRoleAndAgeGreaterThan(String name, Employee.Role role, Integer age);
+
+ List findByRoleAndActiveTrueAndSalaryGreaterThan(Employee.Role role, BigDecimal salary);
+
+ List findByNameContainingIgnoreCaseAndActiveTrueAndAgeBetween(String name, Integer minAge, Integer maxAge);
+
+ List findByCompanyNameContainingAndActiveTrue(String companyName);
+
+ List findBySkillsNameContainingAndAgeGreaterThan(String skillName, Integer age);
+
+ // ==================== 12. DISTINCT 查询 ====================
+
+ List findDistinctByRole(Employee.Role role);
+
+ // ==================== 13. TOP/LIMIT 查询 ====================
+
+ List findTop5BySalaryGreaterThan(BigDecimal salary);
+
+ Optional findFirstByRoleOrderBySalaryDesc(Employee.Role role);
+
+ // ==================== 14. LIKE 模式查询 ====================
+
+ List findByNameLikeIgnoreCase(String pattern);
+
+ List findByNameStartingWithAndRole(String prefix, Employee.Role role);
+
+ List findByNameEndingWithAndAgeLessThan(String suffix, Integer age);
}