Compare commits
5 Commits
53d94c5f4c
...
f81217f14a
| Author | SHA1 | Date | |
|---|---|---|---|
| f81217f14a | |||
| 611988d7ab | |||
| 324d646b27 | |||
| d1392f89a3 | |||
| 93eded44cb |
@@ -132,26 +132,6 @@ public class AbstractTestApplication {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, Object> randomEmployee(Long companyId) {
|
|
||||||
return randomEmployee(companyId, randomString(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String, Object> randomEmployee(Long companyId, String name) {
|
|
||||||
return Map.of(
|
|
||||||
"name", name,
|
|
||||||
"age", randomInt(100),
|
|
||||||
"companyId", companyId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String, Object> randomReport(Long employeeId) {
|
|
||||||
return Map.of(
|
|
||||||
"score", randomDouble(200),
|
|
||||||
"level", randomChar("ABCDE"),
|
|
||||||
"employeeId", employeeId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String randomString(String prefix, int length) {
|
protected String randomString(String prefix, int length) {
|
||||||
return prefix + randomString(length);
|
return prefix + randomString(length);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.service.template.database.common.test.entity;
|
|
||||||
|
|
||||||
public enum Industry {
|
|
||||||
TECHNOLOGY,
|
|
||||||
FINANCE,
|
|
||||||
MEDIA,
|
|
||||||
SERVICE,
|
|
||||||
GOVERNMENT,
|
|
||||||
EDUCATION,
|
|
||||||
HEALTHCARE,
|
|
||||||
CONSTRUCTION,
|
|
||||||
RETAIL,
|
|
||||||
OTHER,
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.service.template.database.common.test.entity;
|
|
||||||
|
|
||||||
public enum Level {
|
|
||||||
A, B, C, D, E
|
|
||||||
}
|
|
||||||
@@ -2,27 +2,36 @@ package com.lanyuanxiaoyao.service.template.database.jpa;
|
|||||||
|
|
||||||
import com.blinkfox.fenix.EnableFenix;
|
import com.blinkfox.fenix.EnableFenix;
|
||||||
import com.lanyuanxiaoyao.service.template.database.common.test.AbstractTestApplication;
|
import com.lanyuanxiaoyao.service.template.database.common.test.AbstractTestApplication;
|
||||||
import com.lanyuanxiaoyao.service.template.database.common.test.entity.Industry;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Address;
|
||||||
import com.lanyuanxiaoyao.service.template.database.common.test.entity.Level;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Address_;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Company_;
|
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.Employee;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee_;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee_;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.QEmployee;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.QEmployee;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Report;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Skill;
|
||||||
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Skill_;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.repository.CompanyRepository;
|
import com.lanyuanxiaoyao.service.template.database.jpa.repository.CompanyRepository;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.repository.EmployeeRepository;
|
import com.lanyuanxiaoyao.service.template.database.jpa.repository.EmployeeRepository;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.repository.ReportRepository;
|
import com.lanyuanxiaoyao.service.template.database.jpa.repository.ReportRepository;
|
||||||
|
import com.querydsl.core.types.dsl.CaseBuilder;
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.hibernate.Session;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -30,10 +39,12 @@ import org.springframework.util.Assert;
|
|||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableFenix
|
@EnableFenix
|
||||||
@EnableJpaAuditing
|
@EnableJpaAuditing
|
||||||
|
@Transactional
|
||||||
public class TestApplication extends AbstractTestApplication {
|
public class TestApplication extends AbstractTestApplication {
|
||||||
private final CompanyRepository companyRepository;
|
private final CompanyRepository companyRepository;
|
||||||
private final EmployeeRepository employeeRepository;
|
private final EmployeeRepository employeeRepository;
|
||||||
private final ReportRepository reportRepository;
|
private final ReportRepository reportRepository;
|
||||||
|
private final Session session;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(TestApplication.class, args);
|
SpringApplication.run(TestApplication.class, args);
|
||||||
@@ -43,7 +54,7 @@ public class TestApplication extends AbstractTestApplication {
|
|||||||
public void runTests() {
|
public void runTests() {
|
||||||
testCrud();
|
testCrud();
|
||||||
testDelete();
|
testDelete();
|
||||||
testSpecification();
|
testQuery();
|
||||||
testNative();
|
testNative();
|
||||||
|
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
@@ -76,110 +87,825 @@ public class TestApplication extends AbstractTestApplication {
|
|||||||
companyRepository.deleteBatchByIds(List.of(cid1, cid2));
|
companyRepository.deleteBatchByIds(List.of(cid1, cid2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSpecification() {
|
private void testQuery() {
|
||||||
formatLog("Added");
|
var factory = new JPAQueryFactory(session);
|
||||||
var company1 = companyRepository.save(Company.builder().name(randomString(5)).members(randomInt(100)).industries(Set.of(Industry.MEDIA, Industry.SERVICE)).build());
|
|
||||||
var company2 = companyRepository.save(Company.builder().name(randomString(5)).members(randomInt(100)).industries(Set.of(Industry.MEDIA, Industry.SERVICE)).build());
|
|
||||||
var employee1 = employeeRepository.save(
|
|
||||||
Employee.builder()
|
|
||||||
.name("Tom")
|
|
||||||
.age(randomInt(100))
|
|
||||||
.role(Employee.Role.USER)
|
|
||||||
.company(company1)
|
|
||||||
.connections(Map.of(
|
|
||||||
Employee.ConnectionType.ADDRESS, randomString(50),
|
|
||||||
Employee.ConnectionType.EMAIL, randomString(20)
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
var employee2 = employeeRepository.save(
|
|
||||||
Employee.builder()
|
|
||||||
.name(randomString(10))
|
|
||||||
.age(randomInt(100))
|
|
||||||
.role(Employee.Role.USER)
|
|
||||||
.company(company2)
|
|
||||||
.connections(Map.of(
|
|
||||||
Employee.ConnectionType.ADDRESS, randomString(50),
|
|
||||||
Employee.ConnectionType.EMAIL, randomString(20)
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
var report1 = reportRepository.save(Report.builder().score(randomDouble(50)).level(Level.B).employeeId(employee1.getId()).build());
|
|
||||||
var report2 = reportRepository.save(Report.builder().score(randomDouble(50)).level(Level.E).employeeId(employee2.getId()).build());
|
|
||||||
|
|
||||||
formatLog("Query");
|
formatLog("准备 Specification 查询的测试数据");
|
||||||
var employees1 = employeeRepository.findAll(
|
var company1 = companyRepository.save(Company.builder().name("TechCorp").members(100).build());
|
||||||
builder -> builder
|
var company2 = companyRepository.save(Company.builder().name("DataInc").members(50).build());
|
||||||
.andIsNotNull(Employee.Fields.name)
|
var company3 = companyRepository.save(Company.builder().name("CloudSys").members(150).build());
|
||||||
.andEquals(Employee.Fields.name, "Tom")
|
|
||||||
.andLike(Employee.Fields.name, "To")
|
// 准备 Skills 数据
|
||||||
.andStartsWith(Employee.Fields.name, "To")
|
var skill1 = Skill.builder().name("Java").description("Java 编程语言").build();
|
||||||
.andEndsWith(Employee.Fields.name, "om")
|
var skill2 = Skill.builder().name("Python").description("Python 编程语言").build();
|
||||||
.andLessThan(Employee.Fields.age, 200)
|
var skill3 = Skill.builder().name("Spring").description("Spring 框架").build();
|
||||||
.andGreaterThanEqual(Employee.Fields.age, 0)
|
var skill4 = Skill.builder().name("MySQL").description("MySQL 数据库").build();
|
||||||
.andIn(Employee.Fields.name, List.of("Tom", "Mike"))
|
|
||||||
.andBetween(Employee.Fields.age, 0, 200)
|
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());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
formatLog("1. 基本比较操作符查询 JPA");
|
||||||
|
// 查找姓名为"Bob"、角色不是ADMIN、年龄在20-30之间、薪资在40000-45000之间的员工
|
||||||
|
var result1_jpa = employeeRepository.findAll((root, query, cb) -> cb.and(
|
||||||
|
cb.equal(root.get(Employee_.name), "Bob"),
|
||||||
|
cb.notEqual(root.get(Employee_.role), Employee.Role.ADMIN),
|
||||||
|
cb.greaterThan(root.get(Employee_.age), 20),
|
||||||
|
cb.lessThan(root.get(Employee_.age), 30),
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("1. 基本比较操作符查询 Fenix");
|
||||||
|
var result1_fenix = employeeRepository.findAll(
|
||||||
|
builder -> builder.andEquals(Employee.Fields.name, "Bob")
|
||||||
|
.andNotEquals(Employee.Fields.role, Employee.Role.ADMIN)
|
||||||
|
.andGreaterThan(Employee.Fields.age, 20)
|
||||||
|
.andLessThan(Employee.Fields.age, 30)
|
||||||
|
.andGreaterThanEqual(Employee.Fields.salary, new BigDecimal("40000.00"))
|
||||||
|
.andLessThanEqual(Employee.Fields.salary, new BigDecimal("45000.00"))
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
Assert.isTrue(employees1.size() == 1, "查询数量错误");
|
Assert.isTrue(result1_fenix.size() == 1, "基本比较操作符查询失败 (%d)".formatted(result1_fenix.size()));
|
||||||
|
|
||||||
var employees2 = employeeRepository.findAll(
|
formatLog("1. 基本比较操作符查询 QueryDSL");
|
||||||
(root, query, builder) ->
|
var result1_querydsl = employeeRepository.findAll(
|
||||||
builder.and(
|
QEmployee.employee.name.eq("Bob")
|
||||||
builder.isNotNull(root.get(Employee_.name)),
|
.and(QEmployee.employee.role.ne(Employee.Role.ADMIN))
|
||||||
builder.equal(root.get(Employee_.name), "Tom"),
|
.and(QEmployee.employee.age.gt(20))
|
||||||
builder.like(root.get(Employee_.name), "To%"),
|
.and(QEmployee.employee.age.lt(30))
|
||||||
builder.lessThan(root.get(Employee_.age), 200),
|
.and(QEmployee.employee.salary.goe(new BigDecimal("40000.00")))
|
||||||
builder.greaterThanOrEqualTo(root.get(Employee_.age), 0),
|
.and(QEmployee.employee.salary.loe(new BigDecimal("45000.00")))
|
||||||
builder.in(root.get(Employee_.NAME)).value(List.of("Tom", "Mike")),
|
);
|
||||||
builder.between(root.get(Employee_.age), 0, 200),
|
Assert.isTrue(result1_querydsl.size() == 1, "基本比较操作符查询失败 (%d)".formatted(result1_querydsl.size()));
|
||||||
builder.isNotEmpty(root.get(Employee_.company).get(Company_.employees)),
|
|
||||||
builder.isMember(Industry.MEDIA, root.get(Employee_.company).get(Company_.industries))
|
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(
|
||||||
|
cb.between(root.get(Employee_.age), 25, 30),
|
||||||
|
cb.between(root.get(Employee_.age), 40, 50).not(),
|
||||||
|
root.get(Employee_.age).in(25, 30, 35),
|
||||||
|
root.get(Employee_.role).in(Employee.Role.USER, Employee.Role.ADMIN),
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("2. 区间和集合操作符查询 Fenix");
|
||||||
|
var result2_fenix = employeeRepository.findAll(
|
||||||
|
builder -> builder.andBetween(Employee.Fields.age, 25, 30)
|
||||||
|
.andNotBetween(Employee.Fields.age, 40, 50)
|
||||||
|
.andIn(Employee.Fields.age, List.of(25, 30, 35))
|
||||||
|
.andIn(Employee.Fields.role, List.of(Employee.Role.USER, Employee.Role.ADMIN))
|
||||||
|
.andNotIn(Employee.Fields.name, List.of("Charlie", "David"))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
Assert.isTrue(result2_fenix.size() == 2, "区间和集合操作符查询失败 (%d)".formatted(result2_fenix.size()));
|
||||||
|
|
||||||
|
formatLog("2. 区间和集合操作符查询 QueryDSL");
|
||||||
|
var result2_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.age.between(25, 30)
|
||||||
|
.and(QEmployee.employee.age.between(40, 50).not())
|
||||||
|
.and(QEmployee.employee.age.in(25, 30, 35))
|
||||||
|
.and(QEmployee.employee.role.in(Employee.Role.USER, Employee.Role.ADMIN))
|
||||||
|
.and(QEmployee.employee.name.in("Charlie", "David").not())
|
||||||
|
.and(QEmployee.employee.name.in(List.of("Charlie", "David")).not())
|
||||||
|
);
|
||||||
|
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(
|
||||||
|
cb.like(root.get(Employee_.name), "A%"),
|
||||||
|
cb.notLike(root.get(Employee_.name), "C%"),
|
||||||
|
cb.like(cb.lower(root.get(Employee_.name)), "%ali%"),
|
||||||
|
cb.like(cb.upper(root.get(Employee_.name)), "%ALI%"),
|
||||||
|
cb.greaterThan(cb.length(root.get(Employee_.name)), 4),
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("3. 字符串操作符查询 Fenix");
|
||||||
|
log.info("Fenix框架当前版本不支持以下字符串操作符:");
|
||||||
|
log.info(" - cb.length() - 字符串长度函数");
|
||||||
|
log.info(" - cb.substring() - 子字符串提取函数");
|
||||||
|
log.info(" - cb.lower() / cb.upper() - 大小写转换函数 (不支持直接在条件中使用)");
|
||||||
|
log.info("Fenix支持的部分实现:");
|
||||||
|
var result3_fenix = employeeRepository.findAll(
|
||||||
|
builder -> builder.andStartsWith(Employee.Fields.name, "A")
|
||||||
|
.andNotStartsWith(Employee.Fields.name, "C")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
log.info("Fenix查询结果: {} 条记录(仅支持部分条件)", result3_fenix.size());
|
||||||
|
|
||||||
|
formatLog("3. 字符串操作符查询 QueryDSL");
|
||||||
|
var result3_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.name.startsWith("A")
|
||||||
|
.and(QEmployee.employee.name.startsWith("C").not())
|
||||||
|
.and(QEmployee.employee.name.toLowerCase().contains("ali"))
|
||||||
|
.and(QEmployee.employee.name.toUpperCase().contains("ALI"))
|
||||||
|
.and(QEmployee.employee.name.length().gt(4))
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
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(
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("4. NULL 和布尔操作符查询 Fenix");
|
||||||
|
log.info("Fenix框架当前版本不支持以下布尔操作符:");
|
||||||
|
log.info(" - cb.isTrue() / cb.isFalse() - 专用布尔判断函数");
|
||||||
|
log.info("Fenix通过equals/notEquals处理布尔字段");
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("4. NULL 和布尔操作符查询 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()));
|
||||||
|
|
||||||
|
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(
|
||||||
|
cb.isNotEmpty(root.get(Employee_.skills)),
|
||||||
|
cb.isEmpty(root.get(Employee_.hobbies)).not(),
|
||||||
|
cb.isMember("Reading", root.get(Employee_.hobbies)),
|
||||||
|
cb.isNotMember("Riding", root.get(Employee_.hobbies)),
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("5. 集合操作符查询 Fenix");
|
||||||
|
log.info("Fenix框架当前版本不支持以下集合操作符:");
|
||||||
|
log.info(" - cb.isNotEmpty() / cb.isEmpty() - 集合非空/空判断");
|
||||||
|
log.info(" - cb.isMember() / cb.isNotMember() - 集合成员判断");
|
||||||
|
log.info(" - cb.size() - 集合大小函数");
|
||||||
|
log.info("这些集合操作在JPA Criteria中需要复杂的join处理,Fenix当前不支持");
|
||||||
|
|
||||||
|
formatLog("5. 集合操作符查询 QueryDSL");
|
||||||
|
var result5_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.skills.isNotEmpty()
|
||||||
|
.and(QEmployee.employee.hobbies.isNotEmpty())
|
||||||
|
.and(QEmployee.employee.hobbies.contains("Reading"))
|
||||||
|
.and(QEmployee.employee.hobbies.contains("Riding").not())
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
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(
|
||||||
|
cb.or(
|
||||||
|
cb.equal(root.get(Employee_.name), "Alice"),
|
||||||
|
cb.equal(root.get(Employee_.name), "Bob")
|
||||||
|
),
|
||||||
|
cb.not(
|
||||||
|
cb.or(
|
||||||
|
cb.equal(root.get(Employee_.name), "Charlie"),
|
||||||
|
cb.equal(root.get(Employee_.name), "David")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
Assert.isTrue(result6_jpa.size() == 2, "逻辑操作符查询失败 (%d)".formatted(result6_jpa.size()));
|
||||||
|
|
||||||
|
formatLog("6. 逻辑操作符查询 Fenix");
|
||||||
|
log.info("Fenix框架当前版本不支持复杂的嵌套OR和NOT组合逻辑");
|
||||||
|
log.info("Fenix支持简单的orEquals,但不支持嵌套的or + not组合");
|
||||||
|
var result6_fenix = employeeRepository.findAll(
|
||||||
|
builder -> builder.orEquals(Employee.Fields.name, "Alice")
|
||||||
|
.orEquals(Employee.Fields.name, "Bob")
|
||||||
|
.andNotIn(Employee.Fields.name, List.of("Charlie", "David"))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
Assert.isTrue(result6_fenix.size() == 3, "逻辑操作符查询失败 (%d)".formatted(result6_fenix.size()));
|
||||||
|
|
||||||
|
formatLog("6. 逻辑操作符查询 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()));
|
||||||
|
|
||||||
|
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(
|
||||||
|
Specification.<Employee>where((root, query, cb) -> cb.isTrue(root.get(Employee_.active)))
|
||||||
|
.and((root, query, cb) -> cb.greaterThan(root.get(Employee_.age), 25))
|
||||||
|
.and((root, query, cb) -> cb.notEqual(root.get(Employee_.role), Employee.Role.ADMIN))
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
formatLog("7. Specification 链式调用查询 Fenix");
|
||||||
|
var result7_fenix = employeeRepository.findAll(
|
||||||
|
builder -> builder.andEquals(Employee.Fields.active, true)
|
||||||
|
.andGreaterThan(Employee.Fields.age, 25)
|
||||||
|
.andNotEquals(Employee.Fields.role, Employee.Role.ADMIN)
|
||||||
|
.orEquals(Employee.Fields.name, "Charlie")
|
||||||
|
.andNotEquals(Employee.Fields.name, "Alice Smith")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
Assert.isTrue(result7_fenix.size() == 2, "Specification 链式调用失败 (%d)".formatted(result7_fenix.size()));
|
||||||
|
|
||||||
|
formatLog("7. Specification 链式调用查询 QueryDSL");
|
||||||
|
var result7_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.active.isTrue()
|
||||||
|
.and(QEmployee.employee.age.gt(25))
|
||||||
|
.and(QEmployee.employee.role.ne(Employee.Role.ADMIN))
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
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) -> {
|
||||||
|
return cb.and(
|
||||||
|
// Company Join 条件
|
||||||
|
cb.equal(root.join(Employee_.company).get(Company_.name), "TechCorp"),
|
||||||
|
cb.notEqual(root.join(Employee_.company).get(Company_.name), "DataInc"),
|
||||||
|
// Skills Join 条件
|
||||||
|
cb.equal(root.join(Employee_.skills).get(Skill_.name), "Java"),
|
||||||
|
cb.notEqual(root.join(Employee_.skills).get(Skill_.name), "MySQL"),
|
||||||
|
// Map Join 条件
|
||||||
|
cb.equal(root.join(Employee_.properties).value(), "Senior"),
|
||||||
|
cb.notEqual(root.join(Employee_.properties).value(), "Junior")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Assert.isTrue(result8_jpa.size() == 1, "Join 操作查询失败 (%d)".formatted(result8_jpa.size()));
|
||||||
|
|
||||||
|
formatLog("8. Join 操作查询 Fenix");
|
||||||
|
log.info("Fenix框架当前版本不支持显式的join操作:");
|
||||||
|
log.info(" - root.join() - 显式关联查询");
|
||||||
|
log.info(" - root.fetch() - 显式抓取查询");
|
||||||
|
log.info(" - Map join (cb.equal(root.join().value(), ...))");
|
||||||
|
log.info("Fenix主要用于单表条件查询,复杂join操作建议使用JPA Specification原生方式或QueryDSL");
|
||||||
|
log.info("可以通过doAny使用原生CriteriaBuilder实现join操作");
|
||||||
|
log.info("注意:由于类型系统的限制,doAny中使用join可能会有类型推断问题");
|
||||||
|
log.info("建议:对于join等复杂查询,直接使用JPA Specification原生方式");
|
||||||
|
|
||||||
|
formatLog("8. Join 操作查询 QueryDSL");
|
||||||
|
var result8_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.company().name.eq("TechCorp")
|
||||||
|
.and(QEmployee.employee.company().name.ne("DataInc"))
|
||||||
|
.and(QEmployee.employee.skills.any().name.eq("Java"))
|
||||||
|
.and(QEmployee.employee.skills.any().name.ne("MySQL"))
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
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) -> {
|
||||||
|
var avgSalarySubquery = query.subquery(Double.class);
|
||||||
|
var avgSubRoot = avgSalarySubquery.from(Employee.class);
|
||||||
|
avgSalarySubquery.select(cb.avg(avgSubRoot.get(Employee_.salary)));
|
||||||
|
|
||||||
|
var countSubquery = query.subquery(Long.class);
|
||||||
|
var countSubRoot = countSubquery.from(Employee.class);
|
||||||
|
countSubquery.select(cb.count(countSubRoot));
|
||||||
|
|
||||||
|
var salary = root.get(Employee_.salary).as(BigDecimal.class);
|
||||||
|
var bonus = root.get(Employee_.bonus).as(BigDecimal.class);
|
||||||
|
var totalCompensation = cb.sum(salary, cb.coalesce(bonus, cb.literal(new BigDecimal("0.00"))));
|
||||||
|
|
||||||
|
return cb.and(
|
||||||
|
cb.greaterThan(root.get(Employee_.salary).as(Double.class), avgSalarySubquery),
|
||||||
|
cb.notEqual(cb.literal(5L), countSubquery),
|
||||||
|
cb.greaterThan(totalCompensation, cb.literal(new BigDecimal("55000.00"))),
|
||||||
|
cb.lessThan(totalCompensation, cb.literal(new BigDecimal("70000.00"))),
|
||||||
|
cb.isTrue(root.get(Employee_.active)),
|
||||||
|
cb.notEqual(root.get(Employee_.name), "David"),
|
||||||
|
cb.greaterThan(root.get(Employee_.age), 28),
|
||||||
|
cb.notEqual(root.get(Employee_.role), Employee.Role.USER)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Assert.isTrue(result9_jpa.isEmpty(), "子查询(聚合函数)+ 数学运算失败 (%d)".formatted(result9_jpa.size()));
|
||||||
|
|
||||||
|
formatLog("9. 子查询和聚合函数查询 Fenix");
|
||||||
|
log.info("Fenix框架当前版本不支持以下高级查询特性:");
|
||||||
|
log.info(" - query.subquery() - 子查询");
|
||||||
|
log.info(" - cb.avg(), cb.count(), cb.sum() - 聚合函数");
|
||||||
|
log.info(" - cb.coalesce() - 空值替换函数");
|
||||||
|
log.info(" - cb.sum() - 数值加法运算");
|
||||||
|
log.info("这些是SQL级别的复杂查询,Fenix主要用于动态条件构建");
|
||||||
|
log.info("可以通过doAny使用原生CriteriaBuilder实现部分聚合操作");
|
||||||
|
|
||||||
|
formatLog("9. 子查询和聚合函数查询 QueryDSL");
|
||||||
|
var avgQuery = factory.select(QEmployee.employee.salary.avg());
|
||||||
|
var countQuery = factory.select(QEmployee.employee.count());
|
||||||
|
var result9_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.salary.gt(avgQuery)
|
||||||
|
.and(countQuery.ne(5L))
|
||||||
|
.and(QEmployee.employee.salary.add(QEmployee.employee.bonus.coalesce(new BigDecimal("0.00"))).gt(new BigDecimal("55000.00")))
|
||||||
|
.and(QEmployee.employee.salary.add(QEmployee.employee.bonus.coalesce(new BigDecimal("0.00"))).lt(new BigDecimal("70000.00")))
|
||||||
|
.and(QEmployee.employee.active.isTrue())
|
||||||
|
.and(QEmployee.employee.name.ne("David"))
|
||||||
|
.and(QEmployee.employee.age.gt(28))
|
||||||
|
.and(QEmployee.employee.role.ne(Employee.Role.USER))
|
||||||
|
);
|
||||||
|
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(
|
||||||
|
(root, query, cb) -> cb.and(
|
||||||
|
cb.isTrue(root.get(Employee_.active)),
|
||||||
|
cb.notEqual(root.get(Employee_.role), Employee.Role.ADMIN),
|
||||||
|
cb.greaterThan(root.get(Employee_.age), 20),
|
||||||
|
cb.lessThan(root.get(Employee_.salary), new BigDecimal("60000.00"))
|
||||||
|
),
|
||||||
|
Sort.by(
|
||||||
|
Sort.Order.desc(Employee_.AGE),
|
||||||
|
Sort.Order.asc(Employee_.NAME)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
Assert.isTrue(employees2.size() == 1, "查询数量错误");
|
Assert.isTrue(result10_jpa.size() == 3, "排序查询失败 (%d)".formatted(result10_jpa.size()));
|
||||||
|
|
||||||
var employees3 = employeeRepository.findAll(
|
formatLog("10. 排序查询 Fenix");
|
||||||
QEmployee.employee.name.isNotNull()
|
log.info("Fenix框架使用Spring Data JPA原生的Sort对象进行排序");
|
||||||
.and(QEmployee.employee.name.eq("Tom"))
|
log.info("Fenix构建查询条件,Sort对象通过repository.findAll()的第二个参数传入");
|
||||||
.and(QEmployee.employee.name.like("To%"))
|
var result10_fenix = employeeRepository.findAll(
|
||||||
.and(QEmployee.employee.name.startsWith("To"))
|
builder -> builder.andEquals(Employee.Fields.active, true)
|
||||||
.and(QEmployee.employee.name.endsWith("om"))
|
.andNotEquals(Employee.Fields.role, Employee.Role.ADMIN)
|
||||||
.and(QEmployee.employee.age.lt(200))
|
.andGreaterThan(Employee.Fields.age, 20)
|
||||||
.and(QEmployee.employee.age.goe(0))
|
.andLessThan(Employee.Fields.salary, new BigDecimal("60000.00"))
|
||||||
.and(QEmployee.employee.name.in("Tom", "Mike"))
|
.build(),
|
||||||
.and(QEmployee.employee.age.between(0, 200))
|
Sort.by(
|
||||||
.and(QEmployee.employee.company().employees.isNotEmpty())
|
Sort.Order.desc(Employee.Fields.age),
|
||||||
.and(QEmployee.employee.company().industries.contains(Industry.MEDIA))
|
Sort.Order.asc(Employee.Fields.name)
|
||||||
.and(QEmployee.employee.connections.containsKey(Employee.ConnectionType.EMAIL))
|
)
|
||||||
);
|
);
|
||||||
Assert.isTrue(employees3.size() == 1, "查询数量错误");
|
Assert.isTrue(result10_fenix.size() == 3, "排序查询失败 (%d)".formatted(result10_fenix.size()));
|
||||||
|
|
||||||
formatLog("Clean");
|
formatLog("10. 排序查询 QueryDSL");
|
||||||
reportRepository.deleteAllInBatch();
|
var result10_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.active.isTrue()
|
||||||
|
.and(QEmployee.employee.role.ne(Employee.Role.ADMIN))
|
||||||
|
.and(QEmployee.employee.age.gt(20))
|
||||||
|
.and(QEmployee.employee.salary.lt(new BigDecimal("60000.00"))),
|
||||||
|
QEmployee.employee.age.desc(),
|
||||||
|
QEmployee.employee.name.asc()
|
||||||
|
);
|
||||||
|
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(
|
||||||
|
(root, query, cb) -> cb.and(
|
||||||
|
cb.isTrue(root.get(Employee_.active)),
|
||||||
|
cb.notEqual(root.get(Employee_.role), Employee.Role.ADMIN),
|
||||||
|
cb.greaterThan(root.get(Employee_.age), 20),
|
||||||
|
cb.lessThan(root.get(Employee_.salary), new BigDecimal("60000.00"))
|
||||||
|
),
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("11. 分页查询 Fenix");
|
||||||
|
log.info("Fenix框架使用Spring Data JPA原生的Pageable对象进行分页");
|
||||||
|
log.info("Fenix构建查询条件,Pageable对象通过repository.findAll()的第二个参数传入");
|
||||||
|
var page11_fenix = employeeRepository.findAll(
|
||||||
|
builder -> builder.andEquals(Employee.Fields.active, true)
|
||||||
|
.andNotEquals(Employee.Fields.role, Employee.Role.ADMIN)
|
||||||
|
.andGreaterThan(Employee.Fields.age, 20)
|
||||||
|
.andLessThan(Employee.Fields.salary, new BigDecimal("60000.00"))
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
formatLog("11. 分页查询 QueryDSL");
|
||||||
|
log.info("QueryDSL支持分页查询:");
|
||||||
|
log.info(" - offset() - 跳过记录数");
|
||||||
|
log.info(" - limit() - 限制记录数");
|
||||||
|
log.info(" - 也可以结合Spring Data JPA的Pageable对象");
|
||||||
|
var page11_querydsl = employeeRepository.findAll(
|
||||||
|
QEmployee.employee.active.isTrue()
|
||||||
|
.and(QEmployee.employee.role.ne(Employee.Role.ADMIN))
|
||||||
|
.and(QEmployee.employee.age.gt(20))
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
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(
|
||||||
|
cb.equal(
|
||||||
|
cb.selectCase()
|
||||||
|
.when(cb.greaterThan(root.get(Employee_.age), 30), "Senior")
|
||||||
|
.when(cb.between(root.get(Employee_.age), 25, 30), "Middle")
|
||||||
|
.otherwise("Junior"),
|
||||||
|
"Senior"
|
||||||
|
),
|
||||||
|
cb.notEqual(
|
||||||
|
cb.selectCase()
|
||||||
|
.when(cb.greaterThan(root.get(Employee_.age), 30), "Senior")
|
||||||
|
.when(cb.between(root.get(Employee_.age), 25, 30), "Middle")
|
||||||
|
.otherwise("Junior"),
|
||||||
|
"Junior"
|
||||||
|
)
|
||||||
|
));
|
||||||
|
Assert.isTrue(result12_jpa.size() == 2, "CASE WHEN 查询失败 (%d)".formatted(result12_jpa.size()));
|
||||||
|
|
||||||
|
formatLog("12. CASE WHEN 条件表达式查询 Fenix");
|
||||||
|
log.info("Fenix框架当前版本不支持CASE WHEN条件表达式:");
|
||||||
|
log.info(" - cb.selectCase() - SQL CASE WHEN表达式");
|
||||||
|
log.info(" - .when() - CASE WHEN条件分支");
|
||||||
|
log.info(" - .otherwise() - CASE ELSE分支");
|
||||||
|
log.info("CASE WHEN是SQL级别的条件表达式,Fenix主要用于动态条件构建");
|
||||||
|
log.info("可以通过doAny使用原生CriteriaBuilder实现CASE WHEN操作");
|
||||||
|
log.info("注意:由于类型系统的限制,doAny中使用CriteriaBuilder的复杂表达式可能会有类型推断问题");
|
||||||
|
log.info("建议:对于CASE WHEN等复杂查询,直接使用JPA Specification原生方式");
|
||||||
|
var result12_fenix = employeeRepository.findAll(builder -> {
|
||||||
|
return builder.doAny(null, null, (cb, root, fieldName, value) -> {
|
||||||
|
// 使用原生JPA Criteria实现CASE WHEN(简化版本)
|
||||||
|
var caseExpr = cb.selectCase()
|
||||||
|
.when(cb.greaterThan(root.get("age"), 30), "Senior")
|
||||||
|
.when(cb.between(root.get("age"), 25, 30), "Middle")
|
||||||
|
.otherwise("Junior");
|
||||||
|
return cb.and(
|
||||||
|
cb.equal(caseExpr, "Senior"),
|
||||||
|
cb.notEqual(caseExpr, "Junior")
|
||||||
|
);
|
||||||
|
}).build();
|
||||||
|
});
|
||||||
|
Assert.isTrue(result12_fenix.size() == 2, "CASE WHEN 查询失败 (%d)".formatted(result12_fenix.size()));
|
||||||
|
|
||||||
|
formatLog("12. CASE WHEN 条件表达式查询 QueryDSL");
|
||||||
|
var caseExpr = new CaseBuilder()
|
||||||
|
.when(QEmployee.employee.age.gt(30)).then("Senior")
|
||||||
|
.when(QEmployee.employee.age.between(25, 30)).then("Middle")
|
||||||
|
.otherwise("Junior");
|
||||||
|
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()));
|
||||||
|
|
||||||
|
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) -> {
|
||||||
|
query.distinct(true);
|
||||||
|
return cb.and(
|
||||||
|
// Company Join 条件
|
||||||
|
cb.equal(root.join(Employee_.company).get(Company_.name), "TechCorp"),
|
||||||
|
cb.notEqual(root.join(Employee_.company).get(Company_.name), "DataInc"),
|
||||||
|
// Skills Join 条件
|
||||||
|
cb.equal(root.join(Employee_.skills).get(Skill_.name), "Java"),
|
||||||
|
cb.notEqual(root.join(Employee_.skills).get(Skill_.name), "MySQL"),
|
||||||
|
// Embedded 对象条件
|
||||||
|
cb.equal(root.get(Employee_.address).get(Address_.city), "Beijing"),
|
||||||
|
cb.notEqual(root.get(Employee_.address).get(Address_.city), "Shanghai"),
|
||||||
|
// 集合条件
|
||||||
|
cb.isNotEmpty(root.get(Employee_.skills)),
|
||||||
|
cb.not(cb.isEmpty(root.get(Employee_.hobbies))),
|
||||||
|
// Map 条件
|
||||||
|
cb.isNotEmpty(root.get(Employee_.properties)),
|
||||||
|
// 日期时间字段查询
|
||||||
|
cb.isNotNull(root.get(Employee_.createdTime)),
|
||||||
|
cb.isNotNull(root.get(Employee_.modifiedTime)),
|
||||||
|
// 其他条件
|
||||||
|
cb.isTrue(root.get(Employee_.active)),
|
||||||
|
cb.notEqual(root.get(Employee_.name), "Alice Smith"),
|
||||||
|
cb.greaterThan(root.get(Employee_.age), 25),
|
||||||
|
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()));
|
||||||
|
|
||||||
|
formatLog("13. 综合多条件查询 Fenix");
|
||||||
|
log.info("Fenix框架不支持综合多条件查询中的复杂join和集合操作:");
|
||||||
|
log.info(" - 显式join操作(company, skills, properties)");
|
||||||
|
log.info(" - 集合isEmpty/isNotEmpty/isMember操作");
|
||||||
|
log.info(" - Map join操作");
|
||||||
|
log.info(" - 嵌入式对象查询(address.city)");
|
||||||
|
log.info("Fenix主要支持简单的单表字段查询");
|
||||||
|
log.info("可以通过doAny使用原生CriteriaBuilder实现复杂综合查询");
|
||||||
|
|
||||||
|
formatLog("13. 综合多条件查询 QueryDSL");
|
||||||
|
var result13_querydsl = employeeRepository.findAll(
|
||||||
|
// Company Join 条件
|
||||||
|
QEmployee.employee.company().name.eq("TechCorp")
|
||||||
|
.and(QEmployee.employee.company().name.ne("DataInc"))
|
||||||
|
// Skills Join 条件
|
||||||
|
.and(QEmployee.employee.skills.any().name.eq("Java"))
|
||||||
|
.and(QEmployee.employee.skills.any().name.ne("MySQL"))
|
||||||
|
// Embedded 对象条件
|
||||||
|
.and(QEmployee.employee.address().city.eq("Beijing"))
|
||||||
|
.and(QEmployee.employee.address().city.ne("Shanghai"))
|
||||||
|
// 集合条件
|
||||||
|
.and(QEmployee.employee.skills.isNotEmpty())
|
||||||
|
.and(QEmployee.employee.hobbies.isNotEmpty())
|
||||||
|
// Map 条件
|
||||||
|
// .and(QEmployee.employee.properties.isNotEmpty())
|
||||||
|
// 日期时间字段查询
|
||||||
|
.and(QEmployee.employee.createdTime.isNotNull())
|
||||||
|
.and(QEmployee.employee.modifiedTime.isNotNull())
|
||||||
|
// 其他条件
|
||||||
|
.and(QEmployee.employee.active.isTrue())
|
||||||
|
.and(QEmployee.employee.name.ne("Alice Smith"))
|
||||||
|
.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()));
|
||||||
|
|
||||||
|
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();
|
employeeRepository.deleteAllInBatch();
|
||||||
companyRepository.deleteAllInBatch();
|
companyRepository.deleteAllInBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testNative() {
|
private void testNative() {
|
||||||
formatLog("Added");
|
|
||||||
var company1 = companyRepository.save(Company.builder().name(randomString(5)).members(randomInt(100)).build());
|
|
||||||
var company2 = companyRepository.save(Company.builder().name(randomString(5)).members(randomInt(100)).build());
|
|
||||||
var company3 = companyRepository.save(Company.builder().name(randomString(5)).members(randomInt(100)).build());
|
|
||||||
var employee1 = employeeRepository.save(Employee.builder().name(randomString(10)).age(randomInt(100)).role(Employee.Role.USER).company(company1).build());
|
|
||||||
var employee2 = employeeRepository.save(Employee.builder().name(randomString(10)).age(randomInt(100)).role(Employee.Role.USER).company(company2).build());
|
|
||||||
var employee3 = employeeRepository.save(Employee.builder().name(randomString(10)).age(randomInt(100)).role(Employee.Role.USER).company(company3).build());
|
|
||||||
|
|
||||||
formatLog("HQL Query");
|
|
||||||
var list = employeeRepository.findAllEmployeeWithCompanyName();
|
|
||||||
Assert.isTrue(list.size() == 3, "数量错误");
|
|
||||||
|
|
||||||
formatLog("SQL Query");
|
|
||||||
var list_native = employeeRepository.findAllEmployeeWithCompanyNameNative();
|
|
||||||
Assert.isTrue(list_native.size() == 3, "数量错误");
|
|
||||||
|
|
||||||
formatLog("Clean");
|
|
||||||
employeeRepository.deleteAllInBatch();
|
|
||||||
companyRepository.deleteAllInBatch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.lanyuanxiaoyao.service.template.database.jpa.controller;
|
package com.lanyuanxiaoyao.service.template.database.jpa.controller;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.service.template.database.common.test.entity.Level;
|
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Report;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Report;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.EmployeeService;
|
import com.lanyuanxiaoyao.service.template.database.jpa.service.EmployeeService;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.service.ReportService;
|
import com.lanyuanxiaoyao.service.template.database.jpa.service.ReportService;
|
||||||
@@ -64,7 +63,7 @@ public class ReportController extends SimpleControllerSupport<Report, ReportCont
|
|||||||
public record SaveItem(
|
public record SaveItem(
|
||||||
Long id,
|
Long id,
|
||||||
Double score,
|
Double score,
|
||||||
Level level,
|
Report.Level level,
|
||||||
Long employeeId
|
Long employeeId
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -74,7 +73,7 @@ public class ReportController extends SimpleControllerSupport<Report, ReportCont
|
|||||||
Long employeeId,
|
Long employeeId,
|
||||||
String employeeName,
|
String employeeName,
|
||||||
Double score,
|
Double score,
|
||||||
Level level
|
Report.Level level
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +82,7 @@ public class ReportController extends SimpleControllerSupport<Report, ReportCont
|
|||||||
Long employeeId,
|
Long employeeId,
|
||||||
String employeeName,
|
String employeeName,
|
||||||
Double score,
|
Double score,
|
||||||
Level level,
|
Report.Level level,
|
||||||
LocalDateTime createdTime,
|
LocalDateTime createdTime,
|
||||||
LocalDateTime modifiedTime
|
LocalDateTime modifiedTime
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Embeddable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@ToString
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class Address {
|
||||||
|
@Column(comment = "街道")
|
||||||
|
private String street;
|
||||||
|
|
||||||
|
@Column(comment = "城市")
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
@Column(comment = "省/州")
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
@Column(comment = "邮政编码")
|
||||||
|
private String zipCode;
|
||||||
|
|
||||||
|
@Column(comment = "国家")
|
||||||
|
private String country;
|
||||||
|
}
|
||||||
@@ -1,19 +1,9 @@
|
|||||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.service.template.database.common.test.entity.Industry;
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.ConstraintMode;
|
|
||||||
import jakarta.persistence.ElementCollection;
|
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
import jakarta.persistence.EnumType;
|
|
||||||
import jakarta.persistence.Enumerated;
|
|
||||||
import jakarta.persistence.ForeignKey;
|
|
||||||
import jakarta.persistence.JoinTable;
|
|
||||||
import jakarta.persistence.OneToMany;
|
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -44,14 +34,4 @@ public class Company extends SimpleEntity {
|
|||||||
private String name;
|
private String name;
|
||||||
@Column(nullable = false, comment = "成员数")
|
@Column(nullable = false, comment = "成员数")
|
||||||
private Integer members;
|
private Integer members;
|
||||||
|
|
||||||
@ElementCollection
|
|
||||||
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
|
||||||
@Enumerated(EnumType.STRING)
|
|
||||||
@Column(nullable = false)
|
|
||||||
private Set<Industry> industries = new HashSet<>();
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "company")
|
|
||||||
@ToString.Exclude
|
|
||||||
private Set<Employee> employees;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,33 @@
|
|||||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.ConstraintMode;
|
import jakarta.persistence.ConstraintMode;
|
||||||
import jakarta.persistence.ElementCollection;
|
import jakarta.persistence.ElementCollection;
|
||||||
|
import jakarta.persistence.Embedded;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
import jakarta.persistence.EnumType;
|
import jakarta.persistence.EnumType;
|
||||||
import jakarta.persistence.Enumerated;
|
import jakarta.persistence.Enumerated;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.ForeignKey;
|
import jakarta.persistence.ForeignKey;
|
||||||
|
import jakarta.persistence.Index;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.JoinTable;
|
import jakarta.persistence.JoinTable;
|
||||||
|
import jakarta.persistence.Lob;
|
||||||
|
import jakarta.persistence.ManyToMany;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.MapKeyEnumerated;
|
import jakarta.persistence.MapKeyEnumerated;
|
||||||
|
import jakarta.persistence.OrderColumn;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.persistence.Version;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -22,8 +35,10 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
|
import org.hibernate.annotations.ColumnDefault;
|
||||||
import org.hibernate.annotations.DynamicInsert;
|
import org.hibernate.annotations.DynamicInsert;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
|
import org.hibernate.annotations.Formula;
|
||||||
import org.hibernate.annotations.SoftDelete;
|
import org.hibernate.annotations.SoftDelete;
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
@@ -39,25 +54,82 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@DynamicInsert
|
@DynamicInsert
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
@Table(comment = "员工")
|
@Table(
|
||||||
|
comment = "员工",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_employee_name", columnList = "name"),
|
||||||
|
@Index(name = "idx_employee_salary", columnList = "salary"),
|
||||||
|
@Index(name = "idx_employee_active", columnList = "active")
|
||||||
|
}
|
||||||
|
)
|
||||||
public class Employee extends SimpleEntity {
|
public class Employee extends SimpleEntity {
|
||||||
@Column(nullable = false, comment = "名称")
|
@Column(nullable = false, length = 100, comment = "名称")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Column(nullable = false, comment = "年龄")
|
@Column(nullable = false, comment = "年龄")
|
||||||
private Integer age;
|
private Integer age;
|
||||||
|
|
||||||
@Column(nullable = false, comment = "角色")
|
@Column(nullable = false, comment = "角色")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private Role role;
|
private Role role;
|
||||||
|
|
||||||
|
@Column(unique = true, length = 50, comment = "工号")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Column(nullable = false, comment = "薪资")
|
||||||
|
private BigDecimal salary;
|
||||||
|
|
||||||
|
@Column(precision = 19, scale = 4, comment = "奖金")
|
||||||
|
private BigDecimal bonus;
|
||||||
|
|
||||||
|
@Column(comment = "是否激活")
|
||||||
|
@ColumnDefault("true")
|
||||||
|
private Boolean active;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
@Column(comment = "简历(大文本)")
|
||||||
|
private String resume;
|
||||||
|
|
||||||
|
@Version
|
||||||
|
private Long version;
|
||||||
|
|
||||||
|
@Column(insertable = false, updatable = false)
|
||||||
|
@Formula("salary + COALESCE(bonus, 0)")
|
||||||
|
private BigDecimal earnings;
|
||||||
|
|
||||||
|
@Embedded
|
||||||
|
private Address address;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
@JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private Company company;
|
private Company company;
|
||||||
|
|
||||||
|
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||||
|
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
|
@ToString.Exclude
|
||||||
|
@Builder.Default
|
||||||
|
private Set<Skill> skills = new HashSet<>();
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@JoinTable(joinColumns = @JoinColumn(nullable = false, foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)))
|
||||||
|
@Column(comment = "兴趣")
|
||||||
|
@OrderColumn
|
||||||
|
@ToString.Exclude
|
||||||
|
@Builder.Default
|
||||||
|
private List<String> hobbies = new ArrayList<>();
|
||||||
|
|
||||||
|
@ElementCollection
|
||||||
|
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
|
@Column(comment = "属性")
|
||||||
|
@Builder.Default
|
||||||
|
private Map<String, String> properties = new HashMap<>();
|
||||||
|
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
@JoinTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT), inverseForeignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
@MapKeyEnumerated(EnumType.STRING)
|
@MapKeyEnumerated(EnumType.STRING)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Builder.Default
|
||||||
private Map<ConnectionType, String> connections = new HashMap<>();
|
private Map<ConnectionType, String> connections = new HashMap<>();
|
||||||
|
|
||||||
public enum Role {
|
public enum Role {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.service.template.database.common.test.entity.Level;
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
@@ -34,6 +33,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@Table(comment = "报告")
|
@Table(comment = "报告")
|
||||||
public class Report extends SimpleEntity {
|
public class Report extends SimpleEntity {
|
||||||
@Column(nullable = false, comment = "分数")
|
@Column(nullable = false, comment = "分数")
|
||||||
|
@Builder.Default
|
||||||
private Double score = 0.0;
|
private Double score = 0.0;
|
||||||
@Column(nullable = false, comment = "等级")
|
@Column(nullable = false, comment = "等级")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@@ -41,4 +41,8 @@ public class Report extends SimpleEntity {
|
|||||||
|
|
||||||
@Column(nullable = false, comment = "员工 ID")
|
@Column(nullable = false, comment = "员工 ID")
|
||||||
private Long employeeId;
|
private Long employeeId;
|
||||||
|
|
||||||
|
public enum Level {
|
||||||
|
A, B, C, D, E
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.lanyuanxiaoyao.service.template.database.jpa.entity;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.EntityListeners;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.experimental.FieldNameConstants;
|
||||||
|
import org.hibernate.annotations.DynamicInsert;
|
||||||
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
|
import org.hibernate.annotations.SoftDelete;
|
||||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
@FieldNameConstants
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Entity
|
||||||
|
@SoftDelete
|
||||||
|
@DynamicUpdate
|
||||||
|
@DynamicInsert
|
||||||
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
|
@Table
|
||||||
|
public class Skill extends SimpleEntity {
|
||||||
|
@Column(nullable = false, length = 100, unique = true, comment = "技能名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(length = 500, comment = "技能描述")
|
||||||
|
private String description;
|
||||||
|
}
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
package com.lanyuanxiaoyao.service.template.database.jpa.repository;
|
package com.lanyuanxiaoyao.service.template.database.jpa.repository;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee;
|
import com.lanyuanxiaoyao.service.template.database.jpa.entity.Employee;
|
||||||
import com.lanyuanxiaoyao.service.template.database.jpa.entity.vo.EmployeeWithCompanyName;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.data.jpa.repository.EntityGraph;
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@SuppressWarnings("NullableProblems")
|
@SuppressWarnings("NullableProblems")
|
||||||
@@ -15,10 +12,4 @@ public interface EmployeeRepository extends SimpleRepository<Employee> {
|
|||||||
@EntityGraph(attributePaths = {"company"})
|
@EntityGraph(attributePaths = {"company"})
|
||||||
@Override
|
@Override
|
||||||
Optional<Employee> findOne(Specification<Employee> specification);
|
Optional<Employee> findOne(Specification<Employee> specification);
|
||||||
|
|
||||||
@Query(value = "select e.name, c.name, e.age, e.role from employee e, company c where e.company_id = c.id and c.deleted = false and e.deleted = false", nativeQuery = true)
|
|
||||||
List<EmployeeWithCompanyName> findAllEmployeeWithCompanyNameNative();
|
|
||||||
|
|
||||||
@Query("select new com.lanyuanxiaoyao.service.template.database.jpa.entity.vo.EmployeeWithCompanyName(employee.name, employee.company.name, employee.age, cast(employee.role as string)) from Employee employee")
|
|
||||||
List<EmployeeWithCompanyName> findAllEmployeeWithCompanyName();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user